'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:

enter image description here

And here is the result I want for example (150x150 or any square size):

enter image description here

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.

docs

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: enter image description here

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