使用 OpenCV 将多个图像调整为无填充的正方形大小

Resize multiple images with OpenCV to square size without padding

我的问题很简单,但我还没有在 Google 上找到任何解决方案,所有答案都是为了添加填充,在我的情况下我不想要...

它基本上是以 WordPress 的方式调整图像大小(智能地调整大小和裁剪)...对于没有垫的方形纵横比...请提前帮助并感谢您。

图片输入示例如下:

这是我想要的结果,例如(150x150 或任何方形尺寸):

这是我迄今为止尝试过的方法:

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")

代码有效,但图像失真...

此答案假定您能够使用 Pillow(因为我无法发表评论来提问),这让问题变得更加简单。

Pillows Image.resize 函数允许您传入一个框,您希望调整后的图像来自该框,这正是您要找的。

来自文档:

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.

这是我的解决方案

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)

这是它的工作原理,因为一张图片胜过一千个字,下面是它的作用的图片:

它检查纵向或横向,因为在纵向中,裁剪区域填充宽度并偏离高度;在景观中反之亦然。如果你真的想要的话,你可以用巧妙的 minmax 语句在一个语句中完成。

这是另一种方法,找到图像的中心并切分两个方向上可用的最大像素。

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