Resize image without distortion OpenCV

I am using python 3 and the latest openCV. I am trying to resize an image using the resize function but after resizing the image is very distorted. Code:

import cv2
file = "/home/tanmay/Desktop/test_image.png"
img = cv2.imread(file , 0)
print(img.shape)
cv2.imshow('img' , img)
k = cv2.waitKey(0)
if k == 27:
    cv2.destroyWindow('img')
resize_img = cv2.resize(img  , (28 , 28))
cv2.imshow('img' , resize_img)
x = cv2.waitKey(0)
if x == 27:
    cv2.destroyWindow('img')

      

Original image 480 x 640 (RGB so I passed 0 to get it in grayscale)

Is there any way to resize it and avoid distortion using OpenCV or any other library? I intend to make a handwritten digit recognizer and I trained my neural network using MNIST data so I need the image to be 28x28.

+25


source to share


9 replies


You can try below. The function will keep the frequency of the original image.

def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
    # initialize the dimensions of the image to be resized and
    # grab the image size
    dim = None
    (h, w) = image.shape[:2]

    # if both the width and height are None, then return the
    # original image
    if width is None and height is None:
        return image

    # check to see if the width is None
    if width is None:
        # calculate the ratio of the height and construct the
        # dimensions
        r = height / float(h)
        dim = (int(w * r), height)

    # otherwise, the height is None
    else:
        # calculate the ratio of the width and construct the
        # dimensions
        r = width / float(w)
        dim = (width, int(h * r))

    # resize the image
    resized = cv2.resize(image, dim, interpolation = inter)

    # return the resized image
    return resized

      

Here is a usage example.



image = image_resize(image, height = 800)

      

Hope it helps.

+56


source


If you need to change the image resolution and keep the aspect ratio, use the imutils function (see documentation). something like this:

img = cv2.imread(file , 0)
img = imutils.resize(img, width=1280)
cv2.imshow('image' , img)

      



hope this helps, good luck!

+7


source


The answer provided by @vijay jha is too case-specific. Also includes additional unnecessary addition. I suggest the fixed code below:

def resize2SquareKeepingAspectRation(img, size, interpolation):
  h, w = img.shape[:2]
  c = None if len(img.shape) < 3 else img.shape[2]
  if h == w: return cv2.resize(img, (size, size), interpolation)
  if h > w: dif = h
  else:     dif = w
  x_pos = int((dif - w)/2.)
  y_pos = int((dif - h)/2.)
  if c is None:
    mask = np.zeros((dif, dif), dtype=img.dtype)
    mask[y_pos:y_pos+h, x_pos:x_pos+w] = img[:h, :w]
  else:
    mask = np.zeros((dif, dif, c), dtype=img.dtype)
    mask[y_pos:y_pos+h, x_pos:x_pos+w, :] = img[:h, :w, :]
  return cv2.resize(mask, (size, size), interpolation)

      

The code resizes the image, making it square and keeping the aspect ratio at the same time. Also the code is suitable for 3-channel (color) images. Usage example:

resized = resize2SquareKeepingAspectRation(img, size, cv2.INTER_AREA)

      

+5


source


Try this simple function in Python that uses OpenCV. just pass in the image and specify the desired square size.

def resize_image(img, size=(28,28)):

    h, w = img.shape[:2]

    if h == w: 
        return cv2.resize(img, size, cv2.INTER_AREA)

    dif = h if h > w else w

    interpolation = cv2.INTER_AREA if dif > (size[0]+size[1])//2 else 
                    cv2.INTER_CUBIC

    x_pos = (dif - w)//2
    y_pos = (dif - h)//2

    if len(img.shape) == 2:
        mask = np.zeros((dif, dif), dtype=img.dtype)
        mask[y_pos:y_pos+h, x_pos:x_pos+w] = img[:h, :w]
    else:
        mask = np.zeros((dif, dif, c), dtype=img.dtype)
        mask[y_pos:y_pos+h, x_pos:x_pos+w, :] = img[:h, :w, :]

    return cv2.resize(mask, size, interpolation)

      

usage: squared_image = get_square (image, size = (28,28))

Explanation: The function takes input of any size and creates an empty square image with the image's height or width, whichever is larger. then it places the original image in the center of the blank image. and then it resizes this square image to the desired size to keep the shape of the original image.

Hope this helps you

+4


source


I have a dataset for hand drawings and I need to create small square images from asymmetric drawings.

enter image description here

Thanks to @vijay jha, I created square images while keeping the aspect ratio of the original image. One problem was that the more you zoomed out, the more information was lost.

From 512x256 to 64x64 it will look like this:

64x64

I modified the original code a bit to scale down the image smoothly.

from skimage.transform import resize, pyramid_reduce


def get_square(image, square_size):

    height, width = image.shape    
    if(height > width):
      differ = height
    else:
      differ = width
    differ += 4

    # square filler
    mask = np.zeros((differ, differ), dtype = "uint8")

    x_pos = int((differ - width) / 2)
    y_pos = int((differ - height) / 2)

    # center image inside the square
    mask[y_pos: y_pos + height, x_pos: x_pos + width] = image[0: height, 0: width]

    # downscale if needed
    if differ / square_size > 1:
      mask = pyramid_reduce(mask, differ / square_size)
    else:
      mask = cv2.resize(mask, (square_size, square_size), interpolation = cv2.INTER_AREA)
    return mask

      

512x256 → 64x64

enter image description here

512x256 → 28x28

enter image description here

+3


source


img = cv2.resize(img, (int(img.shape[1]/2), int(img.shape[0]/2)))

      

resizes the image to half its original size. You can change it for any other relationship. Note that the first argument passed to resize () is img.shape [1], not img.shape [0]. This can be counterintuitive. It's easy to miss this twist and get a very distorted picture.

+2


source


window_height

a value is assigned window_height

by which it computes the variable window_width

while maintaining the aspect ratio of the image. To prevent distortion.

import cv2

def resize(self,image,window_height = 500):
    aspect_ratio = float(image.shape[1])/float(image.shape[0])
    window_width = window_height/aspect_ratio
    image = cv2.resize(image, (int(window_height),int(window_width)))
    return image

img = cv2.imread(img_source)         #image location
img_resized = resize(img,window_height = 800)
cv2.imshow("Resized",img_resized)
cv2.waitKey(0)
cv2.destroyAllWindows()

      

+2


source


Don't point in line with what the original question asks, but I landed here looking for an answer to a similar question.

import cv2
def resize_and_letter_box(image, rows, cols):
    """
    Letter box (black bars) a color image (think pan & scan movie shown 
    on widescreen) if not same aspect ratio as specified rows and cols. 
    :param image: numpy.ndarray((image_rows, image_cols, channels), dtype=numpy.uint8)
    :param rows: int rows of letter boxed image returned  
    :param cols: int cols of letter boxed image returned
    :return: numpy.ndarray((rows, cols, channels), dtype=numpy.uint8)
    """
    image_rows, image_cols = image.shape[:2]
    row_ratio = rows / float(image_rows)
    col_ratio = cols / float(image_cols)
    ratio = min(row_ratio, col_ratio)
    image_resized = cv2.resize(image, dsize=(0, 0), fx=ratio, fy=ratio)
    letter_box = np.zeros((int(rows), int(cols), 3))
    row_start = int((letter_box.shape[0] - image_resized.shape[0]) / 2)
    col_start = int((letter_box.shape[1] - image_resized.shape[1]) / 2)
    letter_box[row_start:row_start + image_resized.shape[0], col_start:col_start + image_resized.shape[1]] = image_resized
    return letter_box

      

+1


source


I just ran into the same problem while preparing a dataset for a neural network, and to avoid image distortion, I created a function that resizes and crops the image minimally to fit the target size. It first chooses whether to crop along the y-axis or x-axis by comparing the aspect ratio of the input image to the target aspect ratio. It then resizes the input image to the destination width or height and then crops it by x or y (each depending on the aspect ratio).

    def crop_and_resize(img, w, h):
        im_h, im_w, channels = img.shape
        res_aspect_ratio = w/h
        input_aspect_ratio = im_w/im_h

        if input_aspect_ratio > res_aspect_ratio:
            im_w_r = int(input_aspect_ratio*h)
            im_h_r = h
            img = cv2.resize(img, (im_w_r , im_h_r))
            x1 = int((im_w_r - w)/2)
            x2 = x1 + w
            img = img[:, x1:x2, :]
        if input_aspect_ratio < res_aspect_ratio:
            im_w_r = w
            im_h_r = int(w/input_aspect_ratio)
            img = cv2.resize(img, (im_w_r , im_h_r))
            y1 = int((im_h_r - h)/2)
            y2 = y1 + h
            img = img[y1:y2, :, :]
        if input_aspect_ratio == res_aspect_ratio:
            img = cv2.resize(img, (w, h))

        return img

      

0


source







All Articles