How to crop an image in OpenCV using Python

Go To StackoverFlow.com

139

How can I crop images, like I've done before in PIL, using OpenCV.

Working example on PIL

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

But how I can do it on OpenCV?

This is what I tried:

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)

But it doesn't work.

I think I incorrectly used getRectSubPix. If this is the case, please explain how I can correctly use this function.

2013-03-23 17:00
by Nolik


339

It's very simple. Use numpy slicing.

import cv2
img = cv2.imread("lenna.png")
crop_img = img[y:y+h, x:x+w]
cv2.imshow("cropped", crop_img)
cv2.waitKey(0)
2013-03-23 17:26
by Froyo
Hmm... But how i can save crop image into variable - Nolik 2013-03-23 18:27
remember that x and y are flipped. I missed this - markroxor 2018-08-26 09:31
Alternatively, if you have defined a crop margin, you can do crop_img = img[margin:-margin, margin:-margin]Woofas 2018-08-28 02:35
coordinate system same as in gimp image editor - nerkn 2018-09-02 07:32
This is great, just be aware that changing cropimg will change img. Otherwise, you should cropimg = img[y:y+h, x:x+w].copy( - user1270710 2018-10-27 00:46


89

i had this question and found another answer here: copy region of interest

If we consider (0,0) as top left corner of image called im with left-to-right as x direction and top-to-bottom as y direction. and we have (x1,y1) as the top-left vertex and (x2,y2) as the bottom-right vertex of a rectangle region within that image, then:

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

here is a comprehensive resource on numpy array indexing and slicing which can tell you more about things like cropping a part of an image. images would be stored as a numpy array in opencv2.

:)

2013-05-29 20:21
by samkhan13


5

Note that, image slicing is not creating a copy of the cropped image but creating a pointer to the roi. If you are loading so many images, cropping the relevant parts of the images with slicing, then appending into a list, this might be a huge memory waste.

Suppose you load N images each is >1MP and you need only 100x100 region from the upper left corner.

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.

Alternatively, you can copy the relevant part by .copy(), so garbage collector will remove im.

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

After finding out this, I realized one of the comments by user1270710 mentioned that but it took me quite some time to find out (i.e., debugging etc). So, I think it worths mentioning.

2018-11-27 10:03
by smttsp


2

Robust crop with opencv copy border function:

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
2018-06-21 07:26
by belgraviton
good call - always use built-in functions when possible - Dan Erez 2018-06-24 17:55


1

here is some code for more robust imcrop ( a bit like in 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
2018-04-23 14:09
by Dan Erez


1

this code crop an image from x=0,y=0 position to 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) 
2018-12-31 09:44
by m.hatami
Ads