'Resize multiple images with OpenCV to square size without padding
My question is simple yet I haven't found any solution on Google, all the answers are for adding padding which in my case I don't want...
It's basically resizing images the WordPress way (resize and crop intelligently)... for square aspect ratio without pad... please help and thank you in advance.
Here is the image input example:
And here is the result I want for example (150x150 or any square size):
This is what I have so far tried:
import cv2
import numpy as np
from os import listdir
from os.path import isfile, join
from pathlib import Path
import argparse
import numpy
mypath = 'images'
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]
images = numpy.empty(len(onlyfiles), dtype=object)
for n in range(0, len(onlyfiles)):
path = join(mypath, onlyfiles[n])
images[n] = cv2.imread(join(mypath, onlyfiles[n]),
cv2.IMREAD_UNCHANGED)
try:
img = cv2.imread(path, 1)
resized_dimensions = (400, 400)
resized_image = cv2.resize(img, resized_dimensions, interpolation=cv2.INTER_AREA)
if not cv2.imwrite('output/' +str(n)+ '.jpg', resized_image):
raise Exception("Could not write image")
except Exception as e:
print(str(e))
print("Images resized Successfully")
The code works but the images are distorted...
Solution 1:[1]
This answer assumes you are able to use Pillow (since I can't comment to ask), which makes this so much more simple.
Pillows Image.resize
function allows you to pass in a box that you want the resized image to come from, which is exactly what you are looking for.
From the docs:
Image.resize(size, resample=None, box=None, reducing_gap=None)[source]¶ Returns a resized copy of this image.Parameters
size – The requested size in pixels, as a 2-tuple: (width, height).
box – An optional 4-tuple of floats providing the source image region to be scaled. The values must be within (0, 0, width, height) rectangle. If omitted or None, the entire source is used.
Here's my solution
from PIL import Image
def smart_resize(input_image, new_size):
width = input_image.width
height = input_image.height
# Image is portrait or square
if height >= width:
crop_box = (0, (height-width)//2, width, (height-width)//2 + width)
return input_image.resize(size = (new_size,new_size),
box = crop_box)
# Image is landscape
if width > height:
crop_box = ((width-height)//2, 0, (width-height)//2 + height, height)
return input_image.resize(size = (new_size,new_size),
box = crop_box)
Here's how it works, and since a picture is worth a thousand words, here's a picture of what it does:
It checks for portrait or landscape because in portrait, the crop area fills the width and is offset from the height; vice versa in landscape. You could probably do it in one statement with clever min
and max
statements if you really wanted.
Solution 2:[2]
Here is another way to do so, finding the center of the image and slicing the maximum pixels available in both directions.
def crop_img(image):
# Get image semiaxes
img_h_saxis = image.shape[0]//2
img_w_saxis = image.shape[1]//2
# Declare crop semiaxis as the maximum pixels available in BOTH directions
crop_saxis = min((img_h_saxis, img_w_saxis))
# Declare center of image
center = (img_h_saxis, img_w_saxis)
# Select maximum pixels from center in both directions
cropped_img = image[(center[0]-crop_saxis): (center[0]+ crop_saxis),
(center[1]-crop_saxis): (center[1]+ crop_saxis)]
# You can include here the resize method
return cropped_img
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Lone Survivr |
Solution 2 |