【问题标题】:How to crop an image in OpenCV using Python如何使用 Python 在 OpenCV 中裁剪图像
【发布时间】:2013-03-13 10:35:45
【问题描述】:

如何使用 OpenCV 裁剪图像,就像我之前在 PIL 中所做的那样。

关于 PIL 的工作示例

im = Image.open('0.png').convert('L')
im = im.crop((1, 1, 98, 33))
im.save('_0.png')

但是我如何在 OpenCV 上做到这一点?

这是我尝试过的:

im = cv.imread('0.png', cv.CV_LOAD_IMAGE_GRAYSCALE)
(thresh, im_bw) = cv.threshold(im, 128, 255, cv.THRESH_OTSU)
im = cv.getRectSubPix(im_bw, (98, 33), (1, 1))
cv.imshow('Img', im)
cv.waitKey(0)

但它不起作用。

我认为我错误地使用了getRectSubPix。如果是这种情况,请解释我如何正确使用此功能。

【问题讨论】:

    标签: python opencv


    【解决方案1】:

    这很简单。使用 numpy 切片。

    import cv2
    img = cv2.imread("lenna.png")
    crop_img = img[y:y+h, x:x+w]
    cv2.imshow("cropped", crop_img)
    cv2.waitKey(0)
    

    【讨论】:

    • 嗯...但是如何将裁剪图像保存到变量中?
    • 记住 x 和 y 是翻转的。我错过了这个。
    • 或者,如果你定义了裁剪边距,你可以crop_img = img[margin:-margin, margin:-margin]
    • 这很好,只是要注意改变crop_img会改变img。否则,你应该crop_img = img[y:y+h, x:x+w].copy()
    • @javadba numpy 实现细节。 Numpy 使用 row, col 表示法而不是 col, row
    【解决方案2】:

    我有这个问题,在这里找到了另一个答案:copy region of interest

    如果我们将 (0,0) 视为名为@9​​87654326@ 的图像的左上角,从左到右为 x 方向,从上到下为 y 方向。我们将 (x1,y1) 作为左上角顶点,将 (x2,y2) 作为该图像中矩形区域的右下角顶点,然后:

    roi = im[y1:y2, x1:x2]
    

    这里是numpy array indexing and slicing 上的综合资源,它可以告诉您更多关于裁剪图像的一部分的信息。图像将在 opencv2 中存储为 numpy 数组。

    :)

    【讨论】:

    • 嗨,在你的情况下不应该是 `roi = im[y1:y2+1, x1:x2+1]` 吗?因为 numpy 使用排除区域来切片。
    • @samkhan13,当我使用这个公式进行裁剪时,我所有的裁剪都有形状(0、宽度、通道)。 IE。我根本没有得到 y 维度
    • @mLstudent33 图像im 可能没有被正确读取并且是空的。尝试使用带断点的 IDE 逐步诊断您的代码。您可以使用 google colab 创建代码块,并可以在 stackoverflow python chat room 上分享您的 jupytor notebook 以获得他人的帮助。
    • @samkhan13 实际上我在 Github Opencv 问题上发布了一个奇怪的问题:github.com/opencv/opencv/issues/15406 我也会查看聊天记录。谢谢!
    【解决方案3】:

    此代码将图像从 x=0,y=0 裁剪为 h=100,w=200。

    import numpy as np
    import cv2
    
    image = cv2.imread('download.jpg')
    y=0
    x=0
    h=100
    w=200
    crop = image[y:y+h, x:x+w]
    cv2.imshow('Image', crop)
    cv2.waitKey(0) 
    

    【讨论】:

    • @hatami,所以高度是 100 像素“低于”y = 0 对吗?它是 numpy 数组的第 101 行吗?并且宽度是 x =0 右边 200 像素对吗?
    • 感谢使用可以实际理解的变量名称。
    【解决方案4】:

    请注意,图像切片不是创建cropped image 的副本,而是创建pointerroi。如果您要加载这么多图像,然后通过切片裁剪图像的相关部分,然后附加到列表中,这可能会浪费大量内存。

    假设您加载 N 张图片,每张图片都是 >1MP,并且您只需要从左上角开始的 100x100 区域。

    Slicing:

    X = []
    for i in range(N):
        im = imread('image_i')
        X.append(im[0:100,0:100]) # This will keep all N images in the memory. 
                                  # Because they are still used.
    

    或者,您可以通过.copy()复制相关部分,因此垃圾收集器将删除im

    X = []
    for i in range(N):
        im = imread('image_i')
        X.append(im[0:100,0:100].copy()) # This will keep only the crops in the memory. 
                                         # im's will be deleted by gc.
    

    发现这一点后,我意识到user1270710 提到的one of the comments 提到了这一点,但我花了很长时间才发现(即调试等)。所以,我认为值得一提。

    【讨论】:

    • 就占用的内存空间而言,我知道复制感兴趣的区域是最好的做法,但是耗时呢?如果我做copy() ROI,与切片相比,结果会是什么?另外,如果我有一个变量tmp 来存储我从计算机加载的每张图片,那么切片应该不会对我的记忆产生不良影响,对吧? 您描述的问题仅与加载所有图像然后再次存储它们的 ROI 时发生的情况有关,同时具有原始图像和 ROI如果我理解正确,请告诉我。
    • 在我所说的情况下,复制的时间可以忽略不计。除非您多次复制大图像,否则您不会有时间差异。在我的代码中,每次裁剪的效果将小于 1 毫秒。问题是您要么存储大图像和指针(ROI 只有几个字节),要么将小图像存储在内存中(在我的情况下)。如果你这样做几次,那很好。但是,如果你这样做数千次,内存使用会因切片而疯狂。就像您在进行切片时加载数千张图像后填满整个内存一样。而如果 MBs,我的代码仍将在订单中
    【解决方案5】:

    具有opencv复制边框功能的强大裁剪:

    def imcrop(img, bbox):
       x1, y1, x2, y2 = bbox
       if x1 < 0 or y1 < 0 or x2 > img.shape[1] or y2 > img.shape[0]:
            img, x1, x2, y1, y2 = pad_img_to_fit_bbox(img, x1, x2, y1, y2)
       return img[y1:y2, x1:x2, :]
    
    def pad_img_to_fit_bbox(img, x1, x2, y1, y2):
        img = cv2.copyMakeBorder(img, - min(0, y1), max(y2 - img.shape[0], 0),
                                -min(0, x1), max(x2 - img.shape[1], 0),cv2.BORDER_REPLICATE)
       y2 += -min(0, y1)
       y1 += -min(0, y1)
       x2 += -min(0, x1)
       x1 += -min(0, x1)
       return img, x1, x2, y1, y2
    

    【讨论】:

    • 你能解释一下这里的 bbox 是什么,我们应该在它的值中给出什么,因为无论我试图传递什么值,它都会在 x1,y1,x2,y2 = bbox 上给我错误,同时说:@987654323 @
    • @sabah 它应该是一个具有 4 个值的元组或列表。如果将 bbox 定义为单个整数,则会出现错误
    【解决方案6】:

    以下是裁剪图像的方法。

    image_path:要编辑的图片路径

    coords: x/y 坐标元组 (x1, y1, x2, y2)[打开图片在 mspaint 并检查视图选项卡中的“标尺”以查看坐标]

    saved_location:保存裁剪图像的路径

    from PIL import Image
        def crop(image_path, coords, saved_location:
            image_obj = Image.open("Path of the image to be cropped")
                cropped_image = image_obj.crop(coords)
                cropped_image.save(saved_location)
                cropped_image.show()
    
    
    if __name__ == '__main__':
        image = "image.jpg"
        crop(image, (100, 210, 710,380 ), 'cropped.jpg')
    

    【讨论】:

      【解决方案7】:

      这里有一些更健壮的 imcrop 代码(有点像 matlab 中的)

      def imcrop(img, bbox): 
          x1,y1,x2,y2 = bbox
          if x1 < 0 or y1 < 0 or x2 > img.shape[1] or y2 > img.shape[0]:
              img, x1, x2, y1, y2 = pad_img_to_fit_bbox(img, x1, x2, y1, y2)
          return img[y1:y2, x1:x2, :]
      
      def pad_img_to_fit_bbox(img, x1, x2, y1, y2):
          img = np.pad(img, ((np.abs(np.minimum(0, y1)), np.maximum(y2 - img.shape[0], 0)),
                     (np.abs(np.minimum(0, x1)), np.maximum(x2 - img.shape[1], 0)), (0,0)), mode="constant")
          y1 += np.abs(np.minimum(0, y1))
          y2 += np.abs(np.minimum(0, y1))
          x1 += np.abs(np.minimum(0, x1))
          x2 += np.abs(np.minimum(0, x1))
          return img, x1, x2, y1, y2
      

      【讨论】:

        【解决方案8】:

        或者,您可以使用 tensorflow 进行裁剪,使用 openCV 从图像制作数组。

        import cv2
        img = cv2.imread('YOURIMAGE.png')
        

        现在img 是一个 (imageheight, imagewidth, 3) 形状数组。使用 tensorflow 裁剪数组:

        import tensorflow as tf
        offset_height=0
        offset_width=0
        target_height=500
        target_width=500
        x = tf.image.crop_to_bounding_box(
            img, offset_height, offset_width, target_height, target_width
        )
        

        用 tf.keras 重新组装图像,这样我们就可以查看它是否有效:

        tf.keras.preprocessing.image.array_to_img(
            x, data_format=None, scale=True, dtype=None
        )
        

        这会在笔记本中打印出图片(在 Google Colab 中测试)。


        整个代码在一起:

        import cv2
        img = cv2.imread('YOURIMAGE.png')
        
        import tensorflow as tf
        offset_height=0
        offset_width=0
        target_height=500
        target_width=500
        x = tf.image.crop_to_bounding_box(
            img, offset_height, offset_width, target_height, target_width
        )
        
        tf.keras.preprocessing.image.array_to_img(
            x, data_format=None, scale=True, dtype=None
        )
        

        【讨论】:

          【解决方案9】:

          通过使用此功能,您可以轻松裁剪图像

          def cropImage(Image, XY: tuple, WH: tuple, returnGrayscale=False):
              # Extract the x,y and w,h values
              (x, y) = XY
              (w, h) = WH
              # Crop Image with numpy splitting
              crop = Image[y:y + h, x:x + w]
              # Check if returnGrayscale Var is true if is then convert image to grayscale
              if returnGrayscale:
                  crop = cv2.cvtColor(crop, cv2.COLOR_BGR2GRAY)
              # Return cropped image
              return crop
          

          希望对您有所帮助

          【讨论】:

            【解决方案10】:

            在代码下方裁剪或感兴趣区域 (ROI) 以供人脸使用

            import cv2 
            face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
            image=cv2.imread("ronaldo.jpg")
            gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
            faces = face_cascade.detectMultiScale(gray, 1.3, 5)
            for (x,y,w,h) in faces:
                 cv2.rectangle(img,(x,y),(x+w,y+h),(255,255,0),2) 
                 roi_image = gray[y:y+h, x:x+w]
            cv2.imshow("crop/region of interset image",roi_image) 
            cv2.waitKey(0)
            cv2.destroyAllWindows()
            

            check for reference

            【讨论】:

              【解决方案11】:
              # Import packages
              import cv2
              
              import numpy as np
              img = cv2.imread('skewness.png')
              print(img.shape) # Print image shape
              
              cv2.imshow("original", img)
              
              # Cropping an image
              cropped_image = img[80:280, 150:330]
               
              # Display cropped image
              cv2.imshow("cropped", cropped_image)
              
              # Save the cropped image
              cv2.imwrite("Cropped Image.jpg", cropped_image)
              
              #The function waitKey waits for a key event infinitely (when \f$\texttt{delay}\leq 0\f$ ) or for delay milliseconds, when it is positive
              cv2.waitKey(0)
              
              #The function destroyAllWindows destroys all of the opened HighGUI windows.
              cv2.destroyAllWindows()
              

              【讨论】:

                【解决方案12】:

                为了方便您,这里是我使用的代码:

                    top=514
                    right=430
                    height= 40
                    width=100
                    croped_image = image[top : (top + height) , right: (right + width)]
                    plt.imshow(croped_image, cmap="gray")
                    plt.show()
                

                【讨论】:

                  猜你喜欢
                  • 2023-03-05
                  • 2019-12-28
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-12-21
                  • 1970-01-01
                  相关资源
                  最近更新 更多