23

I am working with openCv and python and I am dealing with Structural Analysis and Shape Descriptors. I have found this blog: http://opencvpython.blogspot.it/2012/06/contours-2-brotherhood.html that's very helpful and I have tried with a black and white image to drawing a bounding rectangle and it works. But now from an image i extract, for example, the yellow color and on that i would like to draw a bounding rectangle. The problem is that the black and white image is not uniform it has some noise and like that the code doesn't recognize the whole shape.

origianl image

black and white image

final image

And this is the code:

import numpy as np
import cv2

im = cv2.imread('shot.bmp')
hsv_img = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
COLOR_MIN = np.array([20, 80, 80],np.uint8)
COLOR_MAX = np.array([40, 255, 255],np.uint8)
frame_threshed = cv2.inRange(hsv_img, COLOR_MIN, COLOR_MAX)
imgray = frame_threshed
ret,thresh = cv2.threshold(frame_threshed,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnt=contours[0]
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow("Show",im)
cv2.waitKey()
cv2.destroyAllWindows()
Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
Gianfra
  • 1,119
  • 4
  • 17
  • 33
  • Erode and then dilate. http://docs.opencv.org/doc/tutorials/imgproc/erosion_dilatation/erosion_dilatation.html – LovaBill May 14 '13 at 09:08
  • Your original, colour, image doesn't look very clear, maybe this is causing the noise – GPPK May 14 '13 at 09:42
  • yes it is not clear, i took it from a webcam because my last gol is to draw a rectangle in that yellow object in real time. I will try with Eroding and Dilating! – Gianfra May 14 '13 at 09:44

1 Answers1

46

Since your original image is fairly noisy, a simple fix is to remove some of the noise using cv2.medianBlur() This will remove small noise areas in your original image, and leave you with only one contour. The first few lines of your code would look like this:

im = cv2.imread('shot.bmp')
im = cv2.medianBlur(im,5)    # 5 is a fairly small kernel size
hsv_img = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)

However, this method is not the most robust because you must manually specify a kernel size, and the line cnt=contours[0] in your code assumes that the contour of interest is the firs in the list of contours, which is only true if there is only one contour. A more robust method is to assume that you are interested in the largest contour, which will allow you to compensate for even moderate noise.

To do this, add the lines:

# Find the index of the largest contour
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
cnt=contours[max_index]

after the line:

contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

Resulting in this code:

import numpy as np
import cv2

im = cv2.imread('shot.bmp')
hsv_img = cv2.cvtColor(im, cv2.COLOR_BGR2HSV)
COLOR_MIN = np.array([20, 80, 80],np.uint8)
COLOR_MAX = np.array([40, 255, 255],np.uint8)
frame_threshed = cv2.inRange(hsv_img, COLOR_MIN, COLOR_MAX)
imgray = frame_threshed
ret,thresh = cv2.threshold(frame_threshed,127,255,0)
contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)

# Find the index of the largest contour
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
cnt=contours[max_index]

x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow("Show",im)
cv2.waitKey()
cv2.destroyAllWindows()

Both of these methods give a result with a correct bounding box:

Bounding Box Result

N.B.
As of OpenCV 3.x the findContours() method returns 3 results (as can be seen here), so the additional return value should be caught like:

_, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPL‌​E)
SiHa
  • 7,830
  • 13
  • 34
  • 43
Aurelius
  • 11,111
  • 3
  • 52
  • 69
  • 3
    `_, contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)` should be the new syntax just in case anyone came across with reported error: `too many values to unpack` if you are using opencv 3 and python 3. See ref: https://stackoverflow.com/questions/25504964/opencv-python-valueerror-too-many-values-to-unpack – Puriney May 29 '17 at 22:23
  • 2
    I consider @Puriney 's comment really important, as there are several other question in SO that have originated from this problem. The question should be edited to include so – DarkCygnus Jul 04 '17 at 21:50
  • 1
    Upvoted this answer because it was the first help I found about `cv2.boundingRect` and the value it returns. OpenCV documentation lacks many of crucial little details like this... – Przemek D Jun 26 '18 at 13:14
  • Any idea how this can be modified to find a rotated bounding rectangle? – Austin Mar 16 '19 at 03:01