0

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...

5
  • 1
    just the cropping would be easy. (h,w) = im.shape[:2]; a = min(h,w); im[(h-a)//2 : (h+a)//2, (w-a)//2 : (w+a)//2]. resize to taste. -- for resizing at the same time, I'd probably just calculate an affine matrix and use warpAffine -- I won't comment on all that code in the question. looks too complicated. half of it isn't even for resizing. Commented Jan 6, 2022 at 21:24
  • 1
    or just use imagemagick and some shell commands. Commented Jan 6, 2022 at 21:26
  • this is probably a duplicate question... highly likely that someone already asked this in the past 10 years. Commented Jan 6, 2022 at 22:03
  • Are you able to install pillow? Commented Jan 7, 2022 at 4:20
  • Thank you guys for your help, really appreciate it! It's not a duplicate question otherwise I wouldn't have asked it... Thanks again! Commented Jan 7, 2022 at 8:37

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you for your answer! Is there a way to do this using OpenCV? Because I need to do more processing...
Like @Christoph Rackwitz said in their comment, you can use numpy array slicing to crop, since the images are stored as numpy arrays. Then you can just resize the image using cv2.resize()
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.