J. Doe
J. Doe

Reputation: 165

Resizing and Padding Image with specific height and width in python OpenCV gives errors and inaccurate results

So basically I have a directory with 180 images with different width and height and I would like to resize all images to 1280 by 720 here is the script which I am running

import sys
import os
import numpy as np
from os import walk
import cv2

# width to resize
width = int(sys.argv[1])
# height to resize
height = int(sys.argv[2])
# location of the input dataset
input_dir = sys.argv[3]
# location of the output dataset
out_dir = sys.argv[4]

if len(sys.argv) != 5:
    print("Please specify width, height, input directory and output directory.")
    sys.exit(0)

print("Working...")

# get all the pictures in directory
images = []
ext = (".jpeg", ".jpg", ".png")

for (dirpath, dirnames, filenames) in walk(input_dir):
  for filename in filenames:
        if filename.endswith(ext):
            images.append(os.path.join(dirpath, filename))

for image in images:
    img = cv2.imread(image, cv2.IMREAD_UNCHANGED)

    h, w = img.shape[:2]
    pad_bottom, pad_right = 0, 0
    ratio = w / h

if h > height or w > width:
        # shrinking image algorithm
  interp = cv2.INTER_AREA
else:
        # stretching image algorithm
        interp = cv2.INTER_CUBIC

w = width
h = round(w / ratio)
if h > height:
        h = height
        w = round(h * ratio)
pad_bottom = abs(height - h)
pad_right = abs(width - w)

scaled_img = cv2.resize(img, (w, h), interpolation=interp)
padded_img = cv2.copyMakeBorder(
        scaled_img,0,pad_bottom,0,pad_right,borderType=cv2.BORDER_CONSTANT,value=[0,0,0])

cv2.imwrite(os.path.join(out_dir, os.path.basename(image)), padded_img)


print("Completed!")

here is the command python2.7 $python resize_images.py 1280 720 '/home/train/images/bottle_1/' '/home/train/images/bottle_resize/' which gives me error Working... Traceback (most recent call last): File "resize_images.py", line 46, in h = round(w / ratio) ZeroDivisionError: integer division or modulo by zero

Command for python3 $python3 resize_images.py 1280 720 '/home/train/images/bottle_1/' '/home/train/images/bottle_resize/' in result to this command it only resizes one image without giving any errors or warnings. So what could be the reason its not resizing and padding the images if anyone can help me thanks

Upvotes: 0

Views: 6811

Answers (2)

Farshad Amiri
Farshad Amiri

Reputation: 1

Here is a function using Pillow and Opencv which takes an image in numpy.ndarray format and outputs an image also in numpy.ndarray format with your desired height and width without changing aspect ratio and just apply padding. It does not matter which height and width your image has in the first place.

import numpy as np
from PIL import Image
import cv2

def resize_image(image, height, width):
    image = Image.fromarray(np.uint8(image)).convert('RGB')
    MAX_SIZE = (width, height)
    image.thumbnail(MAX_SIZE)
    image = np.asarray(image)
    y_border = max(height - image.shape[0], 0)
    x_border = max(width - image.shape[1], 0)
    top = y_border // 2
    bottom = y_border - top
    left = x_border // 2
    right = x_border - left
    image = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT)
    return image

you can use the function as follows:

image = resize_image(image, 320, 320)
print(image.shape)
plt.imshow(image)

Besides, if you want the function takes image_path as input instead of image in numpy format you can use this:

import numpy as np
from PIL import Image
import cv2

def resize_image(image_path, height, width):
    image = Image.open(image_path)
    MAX_SIZE = (width, height)
    image.thumbnail(MAX_SIZE)
    image = np.asarray(image)
    y_border = max(height - image.shape[0], 0)
    x_border = max(width - image.shape[1], 0)
    top = y_border // 2
    bottom = y_border - top
    left = x_border // 2
    right = x_border - left
    image = cv2.copyMakeBorder(image, top, bottom, left, right, cv2.BORDER_CONSTANT)
    return image

Upvotes: 0

Stef
Stef

Reputation: 30579

As I wrote in my comment above, the indentation is wrong: the for image in images loop ends with the calculation of the ratio. Then you process just the last image in the images list. Last has nothing to do with the file order in your folder, as walk returns the files in arbitrary order.

Following is the code with correct indentation. It works perfectly:

import sys
import os
import numpy as np
from os import walk
import cv2

# width to resize
width = int(sys.argv[1])
# height to resize
height = int(sys.argv[2])
# location of the input dataset
input_dir = sys.argv[3]
# location of the output dataset
out_dir = sys.argv[4]

if len(sys.argv) != 5:
    print("Please specify width, height, input directory and output directory.")
    sys.exit(0)

print("Working...")

# get all the pictures in directory
images = []
ext = (".jpeg", ".jpg", ".png")

for (dirpath, dirnames, filenames) in walk(input_dir):
  for filename in filenames:
        if filename.endswith(ext):
            images.append(os.path.join(dirpath, filename))
            print(filename)

for image in images:
    img = cv2.imread(image, cv2.IMREAD_UNCHANGED)

    h, w = img.shape[:2]
    pad_bottom, pad_right = 0, 0
    ratio = w / h

    if h > height or w > width:
        # shrinking image algorithm
        interp = cv2.INTER_AREA
    else:
        # stretching image algorithm
        interp = cv2.INTER_CUBIC

    w = width
    h = round(w / ratio)
    if h > height:
        h = height
        w = round(h * ratio)
    pad_bottom = abs(height - h)
    pad_right = abs(width - w)

    scaled_img = cv2.resize(img, (w, h), interpolation=interp)
    padded_img = cv2.copyMakeBorder(
        scaled_img,0,pad_bottom,0,pad_right,borderType=cv2.BORDER_CONSTANT,value=[0,0,0])

    cv2.imwrite(os.path.join(out_dir, os.path.basename(image)), padded_img)

print("Completed!")

Upvotes: 2

Related Questions