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.
source to share
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.
source to share
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)
source to share
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
source to share
I have a dataset for hand drawings and I need to create small square images from asymmetric drawings.
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:
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
512x256 â 28x28
source to share
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.
source to share
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()
source to share
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
source to share
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
source to share