16
votes

How can I crop a concave polygon from an image. My Input image look like this.

and the coordinates of closed polygon are [10,150],[150,100],[300,150],[350,100],[310,20],[35,10]. I want region bounded by concave polygon to be cropped using opencv. I searched for other similar questions but I did not able to find correct answer. That's why I am asking it ? Can you help me.

Any help would be highly appreciated.!!!

2
can you post the original image?api55

2 Answers

36
votes

Steps

  1. find region using the poly points
  2. create mask using the poly points
  3. do mask op to crop
  4. add white bg if needed

The code:

# 2018.01.17 20:39:17 CST
# 2018.01.17 20:50:35 CST
import numpy as np
import cv2

img = cv2.imread("test.png")
pts = np.array([[10,150],[150,100],[300,150],[350,100],[310,20],[35,10]])

## (1) Crop the bounding rect
rect = cv2.boundingRect(pts)
x,y,w,h = rect
croped = img[y:y+h, x:x+w].copy()

## (2) make mask
pts = pts - pts.min(axis=0)

mask = np.zeros(croped.shape[:2], np.uint8)
cv2.drawContours(mask, [pts], -1, (255, 255, 255), -1, cv2.LINE_AA)

## (3) do bit-op
dst = cv2.bitwise_and(croped, croped, mask=mask)

## (4) add the white background
bg = np.ones_like(croped, np.uint8)*255
cv2.bitwise_not(bg,bg, mask=mask)
dst2 = bg+ dst


cv2.imwrite("croped.png", croped)
cv2.imwrite("mask.png", mask)
cv2.imwrite("dst.png", dst)
cv2.imwrite("dst2.png", dst2)

Source image:

enter image description here

Result:

enter image description here

12
votes

You can do it in 3 steps:

  1. Create a mask out of the image

    mask = np.zeros((height, width)) points = np.array([[[10,150],[150,100],[300,150],[350,100],[310,20],[35,10]]]) cv2.fillPoly(mask, points, (255))

  2. Apply mask to original image

    res = cv2.bitwise_and(img,img,mask = mask)

  3. Optionally you can remove the crop the image to have a smaller one

    rect = cv2.boundingRect(points) # returns (x,y,w,h) of the rect cropped = res[rect[1]: rect[1] + rect[3], rect[0]: rect[0] + rect[2]]

With this you should have at the end the image cropped

UPDATE

For the sake of completeness here is the complete code:

import numpy as np
import cv2

img = cv2.imread("test.png")
height = img.shape[0]
width = img.shape[1]

mask = np.zeros((height, width), dtype=np.uint8)
points = np.array([[[10,150],[150,100],[300,150],[350,100],[310,20],[35,10]]])
cv2.fillPoly(mask, points, (255))

res = cv2.bitwise_and(img,img,mask = mask)

rect = cv2.boundingRect(points) # returns (x,y,w,h) of the rect
cropped = res[rect[1]: rect[1] + rect[3], rect[0]: rect[0] + rect[2]]

cv2.imshow("cropped" , cropped )
cv2.imshow("same size" , res)
cv2.waitKey(0)

For the colored background version use the code like this:

import numpy as np
import cv2

img = cv2.imread("test.png")
height = img.shape[0]
width = img.shape[1]

mask = np.zeros((height, width), dtype=np.uint8)
points = np.array([[[10,150],[150,100],[300,150],[350,100],[310,20],[35,10]]])
cv2.fillPoly(mask, points, (255))

res = cv2.bitwise_and(img,img,mask = mask)

rect = cv2.boundingRect(points) # returns (x,y,w,h) of the rect
im2 = np.full((res.shape[0], res.shape[1], 3), (0, 255, 0), dtype=np.uint8 ) # you can also use other colors or simply load another image of the same size
maskInv = cv2.bitwise_not(mask)
colorCrop = cv2.bitwise_or(im2,im2,mask = maskInv)
finalIm = res + colorCrop
cropped = finalIm[rect[1]: rect[1] + rect[3], rect[0]: rect[0] + rect[2]]

cv2.imshow("cropped" , cropped )
cv2.imshow("same size" , res)
cv2.waitKey(0)