【问题标题】:Add padding to images to get them into the same shape向图像添加填充以使它们具有相同的形状
【发布时间】:2017-09-09 12:19:13
【问题描述】:

我有一组不同大小的图像(45,50,3), (69,34,3), (34,98,3)。我想为这些图像添加填充,如下所示:

取整张图片的最大宽度和长度,然后把图片放在那个尺寸

import os
import glob
import cv2

input_path="/home/images"
os.chdir(indput_path)
images=glob.glob("*.png")
Length=[]
Width=[]
for img in images:
    img=cv2.imread(img)
    width,length=img.shape[0:2]
    Length.append(length)
    Width.append(width)
W=max(Width)
L=max(Length)

如何在 opencv 中添加填充以使所有图像具有相同的大小?在示例中我给出的图像将得到(69,98,3)的形状

【问题讨论】:

  • @Zindarod。 l 黑白图像的像素只有 0 或 255。我在图像中的大部分字符都是用黑色书写的。所以我需要白色像素填充。但是,我也有一些字符为白色的图像,因此我需要黑色像素填充。如果在 open cv 中有一个技巧来检测然后添加白色或黑色像素填充,我会徘徊

标签: python image opencv image-processing computer-vision


【解决方案1】:

只需使用 Pillow 的 crop_pad()。它会自动调整大小并在需要的地方添加“零”填充 (rgb=(0,0,0) / black),无需图片缩放。

from PIL import Image

img = Image.open(your_file_path)
img.crop_pad((width, height))

【讨论】:

    【解决方案2】:

    这是在 Python/OpenCV/Numpy 中执行此操作的另一种方法。它使用 Numpy 切片将输入图像复制到具有所需输出大小和给定偏移量的新图像中。在这里,我计算偏移量以进行中心填充。我认为这更容易使用宽度、高度、xoffset、yoffset,而不是每边填充多少。

    输入:

    import cv2
    import numpy as np
    
    # read image
    img = cv2.imread('lena.jpg')
    old_image_height, old_image_width, channels = img.shape
    
    # create new image of desired size and color (blue) for padding
    new_image_width = 300
    new_image_height = 300
    color = (255,0,0)
    result = np.full((new_image_height,new_image_width, channels), color, dtype=np.uint8)
    
    # compute center offset
    x_center = (new_image_width - old_image_width) // 2
    y_center = (new_image_height - old_image_height) // 2
    
    # copy img image into center of result image
    result[y_center:y_center+old_image_height, 
           x_center:x_center+old_image_width] = img
    
    # view result
    cv2.imshow("result", result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    # save result
    cv2.imwrite("lena_centered.jpg", result)
    

    【讨论】:

    • 好的答案,但将变量命名为 2 个字母是不可读和不好的做法。
    • @Paul Feakins 那么你会建议什么来代替我所拥有的?
    • “height”、“width”等通常都可以。
    • 在Python例子中,经常会看到,h,w,c = img.shape所以单字母可以,双字母不行?没有人想一直输入高度和宽度。!尽管如此,我确实理解对可读性和理解的关注
    • 我一般会同意你的看法。我只是懒惰,大多数人都知道 w, h 的意思。但我更喜欢为边界框的结果保留那些。如果不是完整的宽度和高度名称,也许 wd 和 ht 可能是更好的选择。感谢您的评论。今后我会努力做得更好。
    【解决方案3】:

    你可以使用:

    image = cv2.copyMakeBorder(src, top, bottom, left, right, borderType)
    

    src 是您的源图像,topbottomleftright 是图像周围的填充。

    您可以在 while 循环中使用 max(sizes) - 图像的大小值来为每个图像添加填充。 边框类型可以是以下之一:

    • cv2.BORDER_CONSTANT
    • cv2.BORDER_REFLECT
    • cv2.BORDER_REFLECT_101
    • cv2.BORDER_DEFAULT
    • cv2.BORDER_REPLICATE
    • cv2.BORDER_WRAP

    cv2.copyMakeBorder tutorial

    【讨论】:

    • 我怎样才能得到左上角和右下角的值。我只有可以从 image.shape 获得的宽度和长度
    • @vincent "你可以使用 max(sizes) - 图片的尺寸值"
    • 您可以只向左侧或顶部添加值,或者向所有方向添加一半的宽度和长度值。比如:顶部=长度/2,底部=长度/2,左侧=宽度/2,右侧=宽度/2
    • @Azadef,请查看我的更新。它没有在维度上执行正确的操作
    • 这肯定需要显示代码以实际获取两个图像的暗淡,然后使用这种方法调整两者的大小(因为一个可能更宽,而另一个更高)。
    【解决方案4】:

    尝试使用此功能:

    from PIL import Image, ImageOps
    
    
    def padding(img, expected_size):
        desired_size = expected_size
        delta_width = desired_size - img.size[0]
        delta_height = desired_size - img.size[1]
        pad_width = delta_width // 2
        pad_height = delta_height // 2
        padding = (pad_width, pad_height, delta_width - pad_width, delta_height - pad_height)
        return ImageOps.expand(img, padding)
    
    
    def resize_with_padding(img, expected_size):
        img.thumbnail((expected_size[0], expected_size[1]))
        # print(img.size)
        delta_width = expected_size[0] - img.size[0]
        delta_height = expected_size[1] - img.size[1]
        pad_width = delta_width // 2
        pad_height = delta_height // 2
        padding = (pad_width, pad_height, delta_width - pad_width, delta_height - pad_height)
        return ImageOps.expand(img, padding)
    
    
    if __name__ == "__main__":
        img = Image.open("./demo.jpg")
        print(img)
        img = resize_with_padding(img, (500, 400))
        print(img.size)
        img.show()
        img.save("resized_img.jpg")
    

    原始图像

    使用填充调整大小后

    https://gist.github.com/BIGBALLON/cb6ab73f6aaaa068ab6756611bb324b2

    【讨论】:

      【解决方案5】:

      这是一个为你做所有事情的函数:

      import cv2
      
      
      def pad_images_to_same_size(images):
          """
          :param images: sequence of images
          :return: list of images padded so that all images have same width and height (max width and height are used)
          """
          width_max = 0
          height_max = 0
          for img in images:
              h, w = img.shape[:2]
              width_max = max(width_max, w)
              height_max = max(height_max, h)
      
          images_padded = []
          for img in images:
              h, w = img.shape[:2]
              diff_vert = height_max - h
              pad_top = diff_vert//2
              pad_bottom = diff_vert - pad_top
              diff_hori = width_max - w
              pad_left = diff_hori//2
              pad_right = diff_hori - pad_left
              img_padded = cv2.copyMakeBorder(img, pad_top, pad_bottom, pad_left, pad_right, cv2.BORDER_CONSTANT, value=0)
              assert img_padded.shape[:2] == (height_max, width_max)
              images_padded.append(img_padded)
      
          return images_padded
      
      

      【讨论】:

      • 适用于灰度和 RGB。谢谢!
      【解决方案6】:

      像这样(填充在openCV上称为边框):

      BLUE = [255,255,255]
      constant= cv2.copyMakeBorder(image.copy(),10,10,10,10,cv2.BORDER_CONSTANT,value=BLUE)
      

      蓝色甚至可以变成白色

      来源: https://docs.opencv.org/3.4/da/d0c/tutorial_bounding_rects_circles.html

      【讨论】:

        【解决方案7】:

        由于我没有看到可接受的答案,而且必须确定函数的顶部、底部、左侧、右侧这一事实,所以我很容易知道什么对我有用。取自:https://jdhao.github.io/2017/11/06/resize-image-to-square-with-padding/

        import cv2
        
        desired_size = 368
        im_pth = "/home/jdhao/test.jpg"
        
        im = cv2.imread(im_pth)
        old_size = im.shape[:2] # old_size is in (height, width) format
        
        ratio = float(desired_size)/max(old_size)
        new_size = tuple([int(x*ratio) for x in old_size])
        
        # new_size should be in (width, height) format
        
        im = cv2.resize(im, (new_size[1], new_size[0]))
        
        delta_w = desired_size - new_size[1]
        delta_h = desired_size - new_size[0]
        top, bottom = delta_h//2, delta_h-(delta_h//2)
        left, right = delta_w//2, delta_w-(delta_w//2)
        
        color = [0, 0, 0]
        new_im = cv2.copyMakeBorder(im, top, bottom, left, right, cv2.BORDER_CONSTANT,
            value=color)
        
        cv2.imshow("image", new_im)
        cv2.waitKey(0)
        cv2.destroyAllWindows()
        

        【讨论】:

        • 如果我希望生成的图像的高度/宽度不同怎么办?例如,仅填充高度,但保持宽度不变。
        • @AnnaVopureta 如果不对其进行测试,我猜想在im = cv2.resize(im, (new_size[1], new_size[0])) 中,您将不得不将其中一个 new_size 更改为 old_size。第一个用于保持高度不变,第二个用于保持宽度
        猜你喜欢
        • 2020-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多