【问题标题】:Resize image to maintain aspect ratio in Python, OpenCv在 Python、OpenCv 中调整图像画布大小以保持正方形纵横比
【发布时间】:2017-11-26 23:36:06
【问题描述】:

我想在 Python 中从任何输入图片中获取 1000 x 1000 的图片,这样输入就不会失去其纵横比。换句话说,我想调整输入的大小,使其较长的尺寸为 1000 像素,并用背景颜色“填充”另一个尺寸,直到它变成 1000 x 1000 正方形。原始的必须在最后的中心。

【问题讨论】:

  • 图片是否保证两个维度都小于1000?否则你会“丢失信息”
  • 不,输入图像可以是任意尺寸。
  • 如果您只是想将一堆图像处理成这种大小,这可以通过 ImageMagick 或类似的单个 bash 命令来完成...无需使用 OpenCV。但如果它只是您算法的一部分,您可以简单地使用 resize 将图像大小调整为 1000 的最大尺寸,然后使用 copyMakeBorder 填充剩余位。

标签: python opencv image-processing aspect-ratio


【解决方案1】:

基于上面 Alexander-Reynolds 的回答,这里是处理所有可能大小和情况的代码。

def resizeAndPad(img, size, padColor=255):

h, w = img.shape[:2]
sh, sw = size

# interpolation method
if h > sh or w > sw: # shrinking image
    interp = cv2.INTER_AREA

else: # stretching image
    interp = cv2.INTER_CUBIC

# aspect ratio of image
aspect = float(w)/h 
saspect = float(sw)/sh

if (saspect > aspect) or ((saspect == 1) and (aspect <= 1)):  # new horizontal image
    new_h = sh
    new_w = np.round(new_h * aspect).astype(int)
    pad_horz = float(sw - new_w) / 2
    pad_left, pad_right = np.floor(pad_horz).astype(int), np.ceil(pad_horz).astype(int)
    pad_top, pad_bot = 0, 0

elif (saspect < aspect) or ((saspect == 1) and (aspect >= 1)):  # new vertical image
    new_w = sw
    new_h = np.round(float(new_w) / aspect).astype(int)
    pad_vert = float(sh - new_h) / 2
    pad_top, pad_bot = np.floor(pad_vert).astype(int), np.ceil(pad_vert).astype(int)
    pad_left, pad_right = 0, 0

# set pad color
if len(img.shape) is 3 and not isinstance(padColor, (list, tuple, np.ndarray)): # color image but only one color provided
    padColor = [padColor]*3

# scale and pad
scaled_img = cv2.resize(img, (new_w, new_h), interpolation=interp)
scaled_img = cv2.copyMakeBorder(scaled_img, pad_top, pad_bot, pad_left, pad_right, borderType=cv2.BORDER_CONSTANT, value=padColor)

return scaled_img

【讨论】:

  • 目前,正方形是白色背景。如何让方框透明背景?
【解决方案2】:

使用 OpenCV

您可以在 OpenCV 中使用resize() 将图像大小调整为您需要的大小。但是,resize() 要求您输入目标大小(在两个维度中)或缩放(在两个维度中),因此您不能只将一个或另一个放入 1000 并让它为您计算另一个.所以最稳健的方法是找到纵横比并计算当较大的尺寸拉伸到 1000 时较小的尺寸是多少。然后您可以调整大小。

h, w = img.shape[:2]
aspect = w/h

请注意,如果aspect 大于1,则图像为水平方向,而如果小于1,则图像为垂直方向(aspect = 1 为方形)。

不同的插值方法看起来会更好,具体取决于您是将图像拉伸到更大的分辨率,还是将其缩小到较低的分辨率。来自resize() 文档:

要缩小图像,通常使用 CV_INTER_AREA 插值效果最佳,而要放大图像,通常使用 CV_INTER_CUBIC(慢)或 CV_INTER_LINEAR(更快但看起来还可以)效果最佳。

因此,在调整大小后,我们最终会得到一个 1000xNNx1000 图像(其中 N&lt;=1000),我们需要在其两侧填充您想要的任何背景颜色以填充图像1000x1000。为此,您可以将copyMakeBorder() 用于纯OpenCV 实现,或者由于您使用的是Python,您可以使用numpy.pad()。您需要决定在需要添加奇数像素以使其成为1000x1000 的情况下该怎么做,例如附加像素是向左还是向右(或顶部或底部,具体取决于方向)您的图像)。

这是一个脚本,它定义了一个 resizeAndPad() 函数,该函数自动计算纵横比,相应地缩放,并根据需要填充,然后在水平、垂直和方形图像上使用它:

import cv2
import numpy as np

def resizeAndPad(img, size, padColor=0):

    h, w = img.shape[:2]
    sh, sw = size

    # interpolation method
    if h > sh or w > sw: # shrinking image
        interp = cv2.INTER_AREA
    else: # stretching image
        interp = cv2.INTER_CUBIC

    # aspect ratio of image
    aspect = w/h  # if on Python 2, you might need to cast as a float: float(w)/h

    # compute scaling and pad sizing
    if aspect > 1: # horizontal image
        new_w = sw
        new_h = np.round(new_w/aspect).astype(int)
        pad_vert = (sh-new_h)/2
        pad_top, pad_bot = np.floor(pad_vert).astype(int), np.ceil(pad_vert).astype(int)
        pad_left, pad_right = 0, 0
    elif aspect < 1: # vertical image
        new_h = sh
        new_w = np.round(new_h*aspect).astype(int)
        pad_horz = (sw-new_w)/2
        pad_left, pad_right = np.floor(pad_horz).astype(int), np.ceil(pad_horz).astype(int)
        pad_top, pad_bot = 0, 0
    else: # square image
        new_h, new_w = sh, sw
        pad_left, pad_right, pad_top, pad_bot = 0, 0, 0, 0

    # set pad color
    if len(img.shape) is 3 and not isinstance(padColor, (list, tuple, np.ndarray)): # color image but only one color provided
        padColor = [padColor]*3

    # scale and pad
    scaled_img = cv2.resize(img, (new_w, new_h), interpolation=interp)
    scaled_img = cv2.copyMakeBorder(scaled_img, pad_top, pad_bot, pad_left, pad_right, borderType=cv2.BORDER_CONSTANT, value=padColor)

    return scaled_img

v_img = cv2.imread('v.jpg') # vertical image
scaled_v_img = resizeAndPad(v_img, (200,200), 127)

h_img = cv2.imread('h.jpg') # horizontal image
scaled_h_img = resizeAndPad(h_img, (200,200), 127)

sq_img = cv2.imread('sq.jpg') # square image
scaled_sq_img = resizeAndPad(sq_img, (200,200), 127)

这给出了图像:

使用 ImageMagick

ImageMagick 是一个简单但构建良好的命令行界面,用于进行基本的图像处理。用一个命令就可以很容易地做你想做的事。有关调整大小命令的说明,请参阅here

$ convert v.jpg -resize 200x200 -background skyblue -gravity center -extent 200x200 scaled-v-im.jpg
$ convert h.jpg -resize 200x200 -background skyblue -gravity center -extent 200x200 scaled-h-im.jpg
$ convert sq.jpg -resize 200x200 -background skyblue -gravity center -extent 200x200 scaled-sq-im.jpg

制作图片:

【讨论】:

  • 我在调整 18x36 图像大小时出错。用aspect = float(w)/h 替换aspect = w/h 修复了它。
  • @Marph 不错的发现。之所以需要它,是因为您使用的是 Python 2。在 Python 2 中,4/2 = 2float(4)/2 = 2.0。在 Python 3 中,4/2 = 2.0,因此不需要强制转换。也就是说,在 Python 2 中,除法是整数除法(所以 5/3 = 1),而在 Python 3 中,它始终是浮点数(所以 5/3 = 1.666666...7)。
  • 如果您使用的是 Python 2,请在脚本开头添加以下行 from __future__ import division
猜你喜欢
  • 2015-04-18
  • 2018-11-04
  • 2013-06-26
  • 2012-04-15
  • 2018-02-24
  • 2012-05-01
相关资源
最近更新 更多