在 Python、OpenCv 中调整图像大小以保持纵横比
Resize image to maintain aspect ratio in Python, OpenCv
我想从任何输入图片中获得一张 Python 的 1000 x 1000 图片,这样输入就不会失去它的纵横比。换句话说,我想调整输入的大小,使其较长的维度为 1000 像素,并用背景颜色“填充”另一个维度,直到它变成 1000 x 1000 平方。原来的一定要在最后居中
使用 OpenCV
您可以在 OpenCV 中使用 resize()
将图像 up/down 调整为您需要的大小。但是,resize()
要求您输入目标大小(在两个维度上)或缩放比例(在两个维度上),因此您不能只将一个或另一个输入 1000 并让它计算另一个为你。因此,最稳健的方法是找到纵横比并计算当较大的尺寸拉伸到 1000 时较小的尺寸是多少。然后您可以调整大小。
h, w = img.shape[:2]
aspect = w/h
请注意,如果 aspect
大于 1,则图像为水平方向,而如果小于 1,则图像为垂直方向(如果 aspect = 1
则为正方形)。
根据您是将图像拉伸到更大的分辨率,还是将其缩小到更低的分辨率,不同的插值方法看起来会更好。来自 resize()
文档:
To shrink an image, it will generally look best with CV_INTER_AREA interpolation, whereas to enlarge an image, it will generally look best with CV_INTER_CUBIC (slow) or CV_INTER_LINEAR (faster but still looks OK).
因此,在调整大小后,我们最终会得到一个 1000xN
或 Nx1000
图像(其中 N<=1000
),我们需要用您想要的任何背景颜色填充它两侧填充图像至1000x1000
。为此,您可以使用 copyMakeBorder()
for a pure OpenCV implementation, or since you're using Python you can use 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
制作图像:
基于上面 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
我想从任何输入图片中获得一张 Python 的 1000 x 1000 图片,这样输入就不会失去它的纵横比。换句话说,我想调整输入的大小,使其较长的维度为 1000 像素,并用背景颜色“填充”另一个维度,直到它变成 1000 x 1000 平方。原来的一定要在最后居中
使用 OpenCV
您可以在 OpenCV 中使用 resize()
将图像 up/down 调整为您需要的大小。但是,resize()
要求您输入目标大小(在两个维度上)或缩放比例(在两个维度上),因此您不能只将一个或另一个输入 1000 并让它计算另一个为你。因此,最稳健的方法是找到纵横比并计算当较大的尺寸拉伸到 1000 时较小的尺寸是多少。然后您可以调整大小。
h, w = img.shape[:2]
aspect = w/h
请注意,如果 aspect
大于 1,则图像为水平方向,而如果小于 1,则图像为垂直方向(如果 aspect = 1
则为正方形)。
根据您是将图像拉伸到更大的分辨率,还是将其缩小到更低的分辨率,不同的插值方法看起来会更好。来自 resize()
文档:
To shrink an image, it will generally look best with CV_INTER_AREA interpolation, whereas to enlarge an image, it will generally look best with CV_INTER_CUBIC (slow) or CV_INTER_LINEAR (faster but still looks OK).
因此,在调整大小后,我们最终会得到一个 1000xN
或 Nx1000
图像(其中 N<=1000
),我们需要用您想要的任何背景颜色填充它两侧填充图像至1000x1000
。为此,您可以使用 copyMakeBorder()
for a pure OpenCV implementation, or since you're using Python you can use 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
制作图像:
基于上面 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