【问题标题】:Resize an image without distortion OpenCV调整图像大小而不失真 OpenCV
【发布时间】:2017-11-22 20:52:17
【问题描述】:

我正在使用 python 3 和最新版本的 openCV。我正在尝试使用提供的调整大小功能调整图像大小,但调整大小后图像非常失真。代码:

import cv2
file = "/home/tanmay/Desktop/test_image.png"
img = cv2.imread(file , 0)
print(img.shape)
cv2.imshow('img' , img)
k = cv2.waitKey(0)
if k == 27:
    cv2.destroyWindow('img')
resize_img = cv2.resize(img  , (28 , 28))
cv2.imshow('img' , resize_img)
x = cv2.waitKey(0)
if x == 27:
    cv2.destroyWindow('img')

原始图像为 480 x 640(RGB 因此我通过 0 使其变为灰度)

有什么办法可以调整它的大小并避免使用 OpenCV 或任何其他库的失真?我打算制作一个手写数字识别器,并且我已经使用 MNIST 数据训练了我的神经网络,因此我需要图像为 28x28。

【问题讨论】:

  • 在没有任何失真的情况下,您有 2 个选项:a) 裁剪图像的一部分以使其具有相同的纵横比。 b) 将图像的一部分(例如黑色像素)添加到图像的两侧,使其具有相同的纵横比。如果您没有相同的纵横比,则无法在不失真的情况下获得它。
  • 您必须确保您传递的新尺寸的纵横比与原始图像相同,并确保使用合适的插值方法
  • 我在原始图像中添加了黑色像素以使其成为 640x640,但是当我调整它的大小时,我仍然得到一个扭曲的图像。我能做什么?
  • 指定插值修复它。谢谢。
  • 请确定您所看到的失真类型。不应该有几何变形。根据调整大小使用的方法,可能会有插值变化。

标签: python image python-3.x opencv


【解决方案1】:

您可以在下面尝试。该函数将保持原始图像的纵横比。

def image_resize(image, width = None, height = None, inter = cv2.INTER_AREA):
    # initialize the dimensions of the image to be resized and
    # grab the image size
    dim = None
    (h, w) = image.shape[:2]

    # if both the width and height are None, then return the
    # original image
    if width is None and height is None:
        return image

    # check to see if the width is None
    if width is None:
        # calculate the ratio of the height and construct the
        # dimensions
        r = height / float(h)
        dim = (int(w * r), height)

    # otherwise, the height is None
    else:
        # calculate the ratio of the width and construct the
        # dimensions
        r = width / float(w)
        dim = (width, int(h * r))

    # resize the image
    resized = cv2.resize(image, dim, interpolation = inter)

    # return the resized image
    return resized

这是一个示例用法。

image = image_resize(image, height = 800)

希望这会有所帮助。

【讨论】:

  • 如果我想改变原始图像的纵横比怎么办?无论原始尺寸如何,我都希望将每张图片都设为 28x28。
  • 那么,直接使用cv2.resize(image, (28,28), interpolation = inter)
  • @TanmayBhatnagar 如果我的回答有助于解决您的问题,您能否也给我投票?
  • 您能解释一下什么是插值吗?我知道它有助于减少由于调整图像大小而发生的错误,但我们如何决定使用哪种技术?只是命中和审判吗?
  • @TanmayBhatnagar 你可以在 cv2.resize docs.opencv.org/3.0-beta/modules/imgproc/doc/… 上参考本教程。
【解决方案2】:

如果您需要修改图像分辨率并保持宽高比,请使用函数imutils(查看文档)。像这样:

img = cv2.imread(file , 0)
img = imutils.resize(img, width=1280)
cv2.imshow('image' , img)

希望对你有帮助,祝你好运!

【讨论】:

  • 但是如何同时修改高度?
  • 代码修改高度以保持纵横比不变。
【解决方案3】:

在使用 OpenCV 的 python 中尝试这个简单的函数。只需传递图像并提及您想要的正方形大小即可。

def resize_image(img, size=(28,28)):

    h, w = img.shape[:2]
    c = img.shape[2] if len(img.shape)>2 else 1

    if h == w: 
        return cv2.resize(img, size, cv2.INTER_AREA)

    dif = h if h > w else w

    interpolation = cv2.INTER_AREA if dif > (size[0]+size[1])//2 else 
                    cv2.INTER_CUBIC

    x_pos = (dif - w)//2
    y_pos = (dif - h)//2

    if len(img.shape) == 2:
        mask = np.zeros((dif, dif), dtype=img.dtype)
        mask[y_pos:y_pos+h, x_pos:x_pos+w] = img[:h, :w]
    else:
        mask = np.zeros((dif, dif, c), dtype=img.dtype)
        mask[y_pos:y_pos+h, x_pos:x_pos+w, :] = img[:h, :w, :]

    return cv2.resize(mask, size, interpolation)

用法: squared_image=get_square(image, size=(28,28))

解释: 函数接受任何大小的输入,它会创建一个正方形的空白图像,大小为图像的高度或宽度,以较大者为准。 然后它将原始图像放置在空白图像的中心。然后它将这个方形图像调整为所需的大小,以便保留原始图像内容的形状。

希望对您有所帮助

【讨论】:

  • jk 未定义
  • 感谢 @Mitali Cyrus 的错误报告。我修好了。
【解决方案4】:

@vijay jha 提供的答案过于具体。还包括额外的不必要的填充。我建议下面的固定代码:

def resize2SquareKeepingAspectRation(img, size, interpolation):
  h, w = img.shape[:2]
  c = None if len(img.shape) < 3 else img.shape[2]
  if h == w: return cv2.resize(img, (size, size), interpolation)
  if h > w: dif = h
  else:     dif = w
  x_pos = int((dif - w)/2.)
  y_pos = int((dif - h)/2.)
  if c is None:
    mask = np.zeros((dif, dif), dtype=img.dtype)
    mask[y_pos:y_pos+h, x_pos:x_pos+w] = img[:h, :w]
  else:
    mask = np.zeros((dif, dif, c), dtype=img.dtype)
    mask[y_pos:y_pos+h, x_pos:x_pos+w, :] = img[:h, :w, :]
  return cv2.resize(mask, (size, size), interpolation)

代码调整图像的大小,使其成为正方形,同时保持纵横比。该代码也适用于 3 通道(彩色)图像。 用法示例:

resized = resize2SquareKeepingAspectRation(img, size, cv2.INTER_AREA)

【讨论】:

  • 目前,正方形是黑色背景。如何让方盒透明背景?
  • @NurzhanNogerbek,您可能希望将图像转换为 RGBA 格式。 A-channel 用于对透明度进行编码。
【解决方案5】:

所有其他答案都使用填充来纠正纵横比,当您尝试为神经网络创建标准化数据集时,这通常是非常糟糕的。下面是一个简单的裁剪和调整大小的实现,它保持纵横比并且不创建填充。

def crop_square(img, size, interpolation=cv2.INTER_AREA):
    h, w = img.shape[:2]
    min_size = np.amin([h,w])

    # Centralize and crop
    crop_img = img[int(h/2-min_size/2):int(h/2+min_size/2), int(w/2-min_size/2):int(w/2+min_size/2)]
    resized = cv2.resize(crop_img, (size, size), interpolation=interpolation)

    return resized

例子:

img2 = crop_square(img, 300)

原文:

调整大小:

【讨论】:

  • 我将它用于 Stylegan2-ada 以调整为方形图像,谢谢它的效果很棒!
【解决方案6】:
img = cv2.resize(img, (int(img.shape[1]/2), int(img.shape[0]/2)))

将图像大小调整为原始大小的一半。您可以将其修改为任何其他比率。 请注意,传递给 resize() 的第一个参数是 img.shape[1],而不是 img.shape[0]。这可能违反直觉。很容易忽略这种反转并得到非常扭曲的画面。

【讨论】:

    【解决方案7】:

    我刚刚在为神经网络准备数据集时遇到了同样的问题,为了避免扭曲图像,我制作了一个函数,可以最小化调整图像大小和裁剪图像以适应目标大小.它首先通过比较输入图像的纵横比与目标纵横比来选择是在 y 还是在 x 中裁剪。然后它将输入图像的大小调整为目标宽度或高度,然后在 x 或 y 中裁剪(每个取决于纵横比的比例)。

        def crop_and_resize(img, w, h):
            im_h, im_w, channels = img.shape
            res_aspect_ratio = w/h
            input_aspect_ratio = im_w/im_h
    
            if input_aspect_ratio > res_aspect_ratio:
                im_w_r = int(input_aspect_ratio*h)
                im_h_r = h
                img = cv2.resize(img, (im_w_r , im_h_r))
                x1 = int((im_w_r - w)/2)
                x2 = x1 + w
                img = img[:, x1:x2, :]
            if input_aspect_ratio < res_aspect_ratio:
                im_w_r = w
                im_h_r = int(w/input_aspect_ratio)
                img = cv2.resize(img, (im_w_r , im_h_r))
                y1 = int((im_h_r - h)/2)
                y2 = y1 + h
                img = img[y1:y2, :, :]
            if input_aspect_ratio == res_aspect_ratio:
                img = cv2.resize(img, (w, h))
    
            return img
    

    【讨论】:

      【解决方案8】:

      与原始问题的要求不太一致,但我来到这里寻找类似问题的答案。

      import cv2
      def resize_and_letter_box(image, rows, cols):
          """
          Letter box (black bars) a color image (think pan & scan movie shown 
          on widescreen) if not same aspect ratio as specified rows and cols. 
          :param image: numpy.ndarray((image_rows, image_cols, channels), dtype=numpy.uint8)
          :param rows: int rows of letter boxed image returned  
          :param cols: int cols of letter boxed image returned
          :return: numpy.ndarray((rows, cols, channels), dtype=numpy.uint8)
          """
          image_rows, image_cols = image.shape[:2]
          row_ratio = rows / float(image_rows)
          col_ratio = cols / float(image_cols)
          ratio = min(row_ratio, col_ratio)
          image_resized = cv2.resize(image, dsize=(0, 0), fx=ratio, fy=ratio)
          letter_box = np.zeros((int(rows), int(cols), 3))
          row_start = int((letter_box.shape[0] - image_resized.shape[0]) / 2)
          col_start = int((letter_box.shape[1] - image_resized.shape[1]) / 2)
          letter_box[row_start:row_start + image_resized.shape[0], col_start:col_start + image_resized.shape[1]] = image_resized
          return letter_box
      

      【讨论】:

        【解决方案9】:

        我有一个手绘数据集,我需要从不对称的图纸中创建小方形图像。

        感谢@vijay jha 我创建了方形图像,同时保持了原始图像的纵横比。但一个问题是,您缩小的越多,丢失的信息就越多。

        512x25664x64 如下所示:

        我稍微修改了original code 以平滑缩小图像。

        from skimage.transform import resize, pyramid_reduce
        
        
        def get_square(image, square_size):
        
            height, width = image.shape    
            if(height > width):
              differ = height
            else:
              differ = width
            differ += 4
        
            # square filler
            mask = np.zeros((differ, differ), dtype = "uint8")
        
            x_pos = int((differ - width) / 2)
            y_pos = int((differ - height) / 2)
        
            # center image inside the square
            mask[y_pos: y_pos + height, x_pos: x_pos + width] = image[0: height, 0: width]
        
            # downscale if needed
            if differ / square_size > 1:
              mask = pyramid_reduce(mask, differ / square_size)
            else:
              mask = cv2.resize(mask, (square_size, square_size), interpolation = cv2.INTER_AREA)
            return mask
        

        512x256 -> 64x64

        512x256 -> 28x28

        【讨论】:

          【解决方案10】:

          代码给出了一个window_height,通过它计算window_width变量,同时保持图像的纵横比。为了防止它有任何失真。

          import cv2
          
          def resize(self,image,window_height = 500):
              aspect_ratio = float(image.shape[1])/float(image.shape[0])
              window_width = window_height/aspect_ratio
              image = cv2.resize(image, (int(window_height),int(window_width)))
              return image
          
          img = cv2.imread(img_source)         #image location
          img_resized = resize(img,window_height = 800)
          cv2.imshow("Resized",img_resized)
          cv2.waitKey(0)
          cv2.destroyAllWindows()
          

          【讨论】:

          • 这对我来说最有意义。
          • 删除self..?
          猜你喜欢
          • 2014-04-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-07-21
          • 1970-01-01
          • 1970-01-01
          • 2013-07-18
          • 1970-01-01
          相关资源
          最近更新 更多