In the previous blog, we discussed how to find the convex hull for a particular shape. In this blog, let’s extend our convex hull knowledge to understand another important function known as Convexity defects. You will find its use in many applications such as hand gesture recognition etc. So, let’s get started.
What are Convexity Defects?
Any deviation of the contour from its convex hull is known as the convexity defect. Let’s understand this with the help of the below image.
Here, the red line shows the convex hull, the grey line represents the contour and the black arrow shows the deviation of the hull from the contour (convexity defect). So, it’s obvious that the convex curve has no convexity defects. Now, let’s discuss how to find the convexity defects using OpenCV-Python.
OpenCV
OpenCV provides a function cv2.convexityDefects() for finding the convexity defects of a contour. This takes as input the contour and its corresponding hull indices and returns an array containing the convexity defects as output. The basic syntax is given below.
1 2 3 4 5 |
convexityDefects = cv2.convexityDefects(contour, convexhull) # contour: input contour # convexhull: indices of the contour points that make the hull. # convexityDefects: an array where each row contains these values - [start point, end point, farthest point, approximate distance to farthest point]. |
The output contains an array where each row contains 4 values [start point, endpoint, farthest point, approximate distance to the farthest point]. Note that the first three values returned are indices of contour.
Now, let’s understand the output returned by the cv2.convexityDefects() function in more detail using the below image. Here, the green line represents the hull while the red line shows the corresponding contour. Clearly, there are convexity defects.
OpenCV algorithm for finding Convexity Defects
- for each pair (H[i], H[i+1]) of adjacent hull points
- for each contour point (C[n]) that lies between [H[i], H[i+1])
- Find the point with the maximum distance from the line joining (H[i], H[i+1])
- store the index of (H[i], H[i+1], max distance point) and the max distance in an array
- for each contour point (C[n]) that lies between [H[i], H[i+1])
- Return the array
Now, let’s take a part of the above image and understand how the above algorithm works. Suppose there are 3 contour points between the first pair of hull points (H[0], H[1]).
OpenCV calculates the distance (d1, d2, d3) as shown below.
- Calculate the unit vector for (H[0], H[1]) and rotate it by 90 degrees
- Then project the vectors formed by joining the contour points (C[1], C[2], C[3]) with H[0] onto the unit vector as shown below. The projection is equal to the distances (d1, d2, d3).
Similarly, repeat for each pair of hull points. The full code can be found here. Now, let’s visualize the convexity defects using OpenCV-Python and the image as shown below.
Steps
- Read an image and find its contours. This can be done as
1 2 3 4 5 6 7 |
import cv2 import numpy as np img = cv2.imread('star.jpg') img_gray = cv2.cvtColor(img,cv.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(img_gray, 127, 255,0) contours, hierarchy = cv2.findContours(thresh,2,1) cnt = contours[0] |
Now, find the convex hull for the particular contour as shown below. Remember to pass the returnPoints flag as False. This will give us the indices of the contour points that make the hull.
1 |
hull = cv2.convexHull(cnt, returnPoints = False) |
After that, find the convexity defects as shown below. This will give us the array as discussed above.
1 |
defects = cv2.convexityDefects(cnt, hull) |
Since the defects array contains the indices of the contour points so, in order to visualize the defects, we need to convert them to coordinates. Here, we iterate over the array rows and draw a line joining the start point and end point, then draw a circle at the farthest point. This is done as below.
1 2 3 4 5 6 7 8 9 10 11 |
for i in range(defects.shape[0]): s,e,f,d = defects[i,0] start = tuple(cnt[s][0]) end = tuple(cnt[e][0]) far = tuple(cnt[f][0]) cv.line(img,start,end,[0,255,0],2) cv.circle(img,far,5,[0,0,255],-1) cv2.imshow('final_img',img) cv2.waitKey(0) |
I hope you understood the convexity defects and how OpenCV calculates these defects. Hope you enjoy reading.
If you have any doubts/suggestions please feel free to ask and I will do my best to help or improve myself. Good-bye until next time.
Thanks for the easy explanation.