【问题标题】:How to scale the image along X and Y axis and crop to a specific height and width?如何沿 X 和 Y 轴缩放图像并裁剪到特定的高度和宽度?
【发布时间】:2017-06-07 10:51:59
【问题描述】:

我需要在 X 和 Y 方向拉伸图像,但同时保持目标图像大小为 300x300。我正在使用 Open CV。每次我缩放图像然后调整它的大小时,我都会失去缩放,它看起来和我喂它缩放的图像一样。我该如何做到这一点?

这是缩放的代码

def scale(infile,outfile,scx,scy):
    img = cv2.imread(infile,0)
    height, width = img.shape[:2]
    #aspectRatio = width / height
    rows,cols = img.shape
    if scx > 1 or scy > 1:
        scimg = cv2.resize(img,None, fx = scx, fy = scy, interpolation = cv2.INTER_LINEAR)
    else:
        scimg = cv2.resize(img,None, fx = scx, fy = scy, interpolation = cv2.INTER_CUBIC)

    #cheight, cwidth = scimg.shape[:2]
    #area = cheight * cwidth
    #nheight = math.sqrt(area / aspectRatio)
    #nwidth = nheight * aspectRatio
    #cv2.resize(scimg,((int)(nwidth), (int)(nheight)), interpolation = cv2.INTER_CUBIC)
    #top = (int) (0.2*rows)
    #bottom = (int) (0.2*rows)
    #left = (int) (0.2*cols)
    #right = (int) (0.2*cols);
    #cv2.copyMakeBorder(scimg,top,left,bottom,right,cv2.BORDER_CONSTANT,value=255)
    cv2.imwrite(outfile,scimg)

我使用 scales 数组作为 x 和 y 比例因子:scales = [(1,2),(2,1)] 我希望最终图像应该是 300x300 并且应该包含图像的缩放版本。 谢谢!!

【问题讨论】:

  • 这个问题没有多大意义,你想拉伸图像但又想保持相同大小?你的意思是你想缩放图像但裁剪到原始大小?请发布您当前拥有的任何代码以及您想要实现的预期输出。
  • 谢谢。我编辑了问题并添加了我正在使用的代码。
  • 如果拉伸图像,它会更大。是否要拉伸它,然后裁剪原始大小的部分图像?
  • 是的,但可以将其想象为缩小然后将其裁剪为 300x300。我希望 300x300 图像捕捉到图像已缩放并且与原始图像不同。可以做一些事情,比如填充然后调整大小或对图像进行上采样然后裁剪它吗?这是为了增强图像。
  • 嗯,你可以通过两种方式实现这一点。一种方法是将显示器的 窗口大小 设置为您的原始图像大小,并在其中显示您的图像;但这仅适用于显示目的。另一种方法是创建一个零矩阵(或任何值),在其中放置一个与缩放图像大小相同的感兴趣区域,然后将缩放图像放置在 ROI 内。我将发布两种解决方案的答案。

标签: python opencv image-processing


【解决方案1】:

如果您只想将其用于显示目的,最简单的方法是在裁剪后的图像上使用cv2.imshow(),并使用cv2.resizeWindow() 将窗口大小设置为原始图像大小。

import cv2
import numpy as np

img = cv2.imread('lena.png')
h, w = img.shape[:2]
scale = (0.6, 0.4) # define your scale
scaled_img = cv2.resize(img, None, fx=scale[0], fy=scale[1]) # scale image

cv2.namedWindow("Scaled image", cv2.WINDOW_NORMAL) # create a resizeable window
cv2.imshow("Scaled image", scaled_img) # display the image in the window
cv2.resizeWindow("Scaled image", w, h) # resize the window
cv2.waitKey(0)

将在窗口内显示一个缩放的图像,该窗口具有原始图像大小。即使您通过裁剪图像的右侧和顶部(图像锚定在窗口的左下角)来放大图像,这也将起作用。查看输出窗口:

但是,如果您希望将其保存到另一个矩阵中,则需要在图像之外使用您想要的任何颜色填充图像。最简单的方法是使用该颜色创建一个矩阵,然后创建一个与缩放图像大小相同的 ROI,并将缩放图像放置在 ROI 内。

import cv2
import numpy as np

img = cv2.imread('lena.png')
scale = (0.6, 0.4) # define your scale
scaled_img = cv2.resize(img, None, fx=scale[0], fy=scale[1]) # scale image

sh, sw = scaled_img.shape[:2] # get h, w of scaled image
padded_scaled = np.zeros(img.shape, dtype=np.uint8)
padded_scaled[0:sh, 0:sw] = scaled_img

cv2.imshow("Scaled image", padded_scaled)
cv2.waitKey(0)

这会将缩放后的图像放在目标padded_scaled 中的0,0

如果您希望它显示在中心,您可以做一些简单的计算来获得图像的中心,然后将缩放图像的宽度和高度的一半移动到起始位置。

import cv2
import numpy as np

img = cv2.imread('lena.png')
scale = (0.6, 0.4) # define your scale
scaled_img = cv2.resize(img, None, fx=scale[0], fy=scale[1]) # scale image

h, w = img.shape[:2] # get h, w of image
sh, sw = scaled_img.shape[:2] # get h, w of scaled image
center_y = int(h/2 - sh/2)
center_x = int(w/2 - sw/2)
padded_scaled = np.zeros(img.shape, dtype=np.uint8) # using img.shape to obtain #channels
padded_scaled[center_y:center_y+sh, center_x:center_x+sw] = scaled_img

cv2.imshow("Scaled image", padded_scaled)
cv2.waitKey(0)

这会将缩放后的图像放置在填充矩阵的中心:


这仅在图像缩放小于比原始图像时有效,因为您将较小的图像放入较大的空白矩阵中。但是,如果您想允许更大的缩放比例,那么此时您需要进行裁剪。您可以从与原始图像大小相同的缩放图像中裁剪一个区域。但是,如果比例在一个维度上大于 1 而在另一个维度上小于 1,则需要同时裁剪 填充。我将其分为if 语句的两个部分:首先创建一个最多为图像大小的结果,如有必要,通过裁剪,然后填充,如果它更小。

import cv2
import numpy as np

img = cv2.imread('lena.png')
scale_x, scale_y = 13, .3 # define your scale
h, w = img.shape[:2] # get h, w of image

if scale_x > 1 or scale_y > 1:

    scaled_img = cv2.resize(img, None, fx=scale_x, fy=scale_y, interpolation = cv2.INTER_LINEAR) # scale image
    sh, sw = scaled_img.shape[:2] # get h, w of scaled image
    center_y = int(sh/2 - h/2)
    center_x = int(sw/2 - w/2)
    cropped = scaled_img[center_y:center_y+h, center_x:center_x+w]

    result = cropped

elif scale_x > 0 or scale_y > 0:
    scaled_img = cv2.resize(img, None, fx=scale_x, fy=scale_y, interpolation = cv2.INTER_CUBIC) # scale image
    result = scaled_img

else: # scale_x or scale_y is negative
    print("Scales must be greater than 0; returning the original image.")
    result = img

if result.shape < img.shape: # one of the dimensions was scaled smaller, so need to pad
    sh, sw = result.shape[:2] # get h, w of cropped, scaled image
    center_y = int(h/2 - sh/2)
    center_x = int(w/2 - sw/2)
    padded_scaled = np.zeros(img.shape, dtype=np.uint8)
    padded_scaled[center_y:center_y+sh, center_x:center_x+sw] = result
    result = padded_scaled

cv2.imshow("Scaled padded/cropped image", result)
cv2.waitKey(0)

如果在一个方向上拉伸较大,而在另一个方向上缩放较小,这将导致图像显示如下:

【讨论】:

  • 感谢您的回答!!但不幸的是,我遇到了错误,例如 - 当您尝试将缩放图像放入填充图像时,无法将输入数组从形状 (xx,xx) 广播到形状 (xx,xx) 中。另外,我的输入图像不是 300x300。它的大小是任意的。
  • 您可以实际发布这些形状数字作为示例吗?您可能没有正确索引(可能其中一张图像是一个通道,另一张是三通道?)
  • 是的,当然。这是实际错误 - 无法将输入数组从形状 (1336,820) 广播到形状 (334,820)。我确实以灰度加载图像,即在参数读取中添加 0 参数。
  • @Hooli 让我们continue this discussion in chat。如果有必要,我会在我们弄清楚这个问题后编辑帖子。
  • @Hooli 我为图像在任何方向上放大的情况添加了代码。该方法是先裁剪一个部分,如果该裁剪较小,则填充。如果这一切都适合您,您可以选择我的答案作为解决方案吗?谢谢!
猜你喜欢
  • 1970-01-01
  • 2015-03-02
  • 1970-01-01
  • 2016-03-11
  • 1970-01-01
  • 2015-07-02
  • 1970-01-01
  • 2018-11-02
  • 1970-01-01
相关资源
最近更新 更多