A few weeks ago, I demonstrated how to order the (x, y)-coordinates of a rotated bounding box in a clockwise fashion — an extremely useful skill that is critical in many computer vision applications, including (but not limited to) perspective transforms and computing the dimensions of an object in an image.
One PyImageSearch reader emailed in, curious about this clockwise ordering, and posed a similar question:
Is it possible to find the extreme north, south, east, and west coordinates from a raw contour?
“Of course it is!”, I replied.
Today, I’m going to share my solution to find extreme points along a contour with OpenCV and Python.
Finding extreme points in contours with OpenCV
In the remainder of this blog post, I am going to demonstrate how to find the extreme north, south, east, and west (x, y)-coordinates along a contour, like in the image at the top of this blog post.
While this skill isn’t inherently useful by itself, it’s often used as a pre-processing step to more advanced computer vision applications. A great example of such an application is hand gesture recognition:
In the figure above, we have segmented the skin/hand from the image, computed the convex hull (outlined in blue) of the hand contour, and then found the extreme points along the convex hull (red circles).
By computing the extreme points along the hand, we can better approximate the palm region (highlighted as a blue circle):
Which in turn allows us to recognize gestures, such as the number of fingers we are holding up:
Note: I cover how to recognize hand gestures inside the PyImageSearch Gurus course, so if you’re interested in learning more, be sure to claim your spot in line for the next open enrollment!
Implementing such a hand gesture recognition system is outside the scope of this blog post, so we’ll instead utilize the following image:
Where our goal is to compute the extreme points along the contour of the hand in the image.
Let’s go ahead and get started. Open up a new file, name it extreme_points.py
, and let’s get coding:
# import the necessary packages import imutils import cv2 # load the image, convert it to grayscale, and blur it slightly image = cv2.imread("hand_01.png") gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) gray = cv2.GaussianBlur(gray, (5, 5), 0) # threshold the image, then perform a series of erosions + # dilations to remove any small regions of noise thresh = cv2.threshold(gray, 45, 255, cv2.THRESH_BINARY)[1] thresh = cv2.erode(thresh, None, iterations=2) thresh = cv2.dilate(thresh, None, iterations=2) # find contours in thresholded image, then grab the largest # one cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) c = max(cnts, key=cv2.contourArea)
Lines 2 and 3 import our required packages. We then load our example image from disk, convert it to grayscale, and blur it slightly.
Line 12 performs thresholding, allowing us to segment the hand region from the rest of the image. After thresholding, our binary image looks like this:
In order to detect the outlines of the hand, we make a call to cv2.findContours
, followed by sorting the contours to find the largest one, which we presume to be the hand itself (Lines 18-21).
Before we can find extreme points along a contour, it’s important to understand that a contour is simply a NumPy array of (x, y)-coordinates. Therefore, we can leverage NumPy functions to help us find the extreme coordinates.
# determine the most extreme points along the contour extLeft = tuple(c[c[:, :, 0].argmin()][0]) extRight = tuple(c[c[:, :, 0].argmax()][0]) extTop = tuple(c[c[:, :, 1].argmin()][0]) extBot = tuple(c[c[:, :, 1].argmax()][0])
For example, Line 24 finds the smallest x-coordinate (i.e., the “west” value) in the entire contour array c
by calling argmin()
on the x-value and grabbing the entire (x, y)-coordinate associated with the index returned by argmin()
.
Similarly, Line 25 finds the largest x-coordinate (i.e., the “east” value) in the contour array using the argmax()
function.
Lines 26 and 27 perform the same operation, only for the y-coordinate, giving us the “north” and “south” coordinates, respectively.
Now that we have our extreme north, south, east, and west coordinates, we can draw them on our image
:
# draw the outline of the object, then draw each of the # extreme points, where the left-most is red, right-most # is green, top-most is blue, and bottom-most is teal cv2.drawContours(image, [c], -1, (0, 255, 255), 2) cv2.circle(image, extLeft, 8, (0, 0, 255), -1) cv2.circle(image, extRight, 8, (0, 255, 0), -1) cv2.circle(image, extTop, 8, (255, 0, 0), -1) cv2.circle(image, extBot, 8, (255, 255, 0), -1) # show the output image cv2.imshow("Image", image) cv2.waitKey(0)
Line 32 draws the outline of the hand in yellow, while Lines 33-36 draw circles for each of the extreme points, detailed below:
- West: Red
- East: Green
- North: Blue
- South: Teal
Finally, Lines 39 and 40 display the results to our screen.
To execute our script, make sure you download the code and images associated with this post (using the “Downloads” form found at the bottom of this tutorial), navigate to your code directory, and then execute the following command:
$ python extreme_points.py
Your should then see the following out image:
As you can see we have successfully labeled each of the extreme points along the hand. The western-most point is labeled in red, the northern-most point in blue, the eastern-most point in green, and finally the southern-most point in teal.
Below we can see a second example of labeling the extreme points a long a hand:
Let’s examine one final instance:
And that’s all there is to it!
Just keep in mind that the contours list returned by cv2.findContours
is simply a NumPy array of (x, y)-coordinates. By calling argmin()
and argmax()
on this array, we can extract the extreme (x, y)-coordinates.
What's next? I recommend PyImageSearch University.
30+ total classes • 39h 44m video • Last updated: 12/2021
★★★★★ 4.84 (128 Ratings) • 3,000+ Students Enrolled
I strongly believe that if you had the right teacher you could master computer vision and deep learning.
Do you think learning computer vision and deep learning has to be time-consuming, overwhelming, and complicated? Or has to involve complex mathematics and equations? Or requires a degree in computer science?
That’s not the case.
All you need to master computer vision and deep learning is for someone to explain things to you in simple, intuitive terms. And that’s exactly what I do. My mission is to change education and how complex Artificial Intelligence topics are taught.
If you're serious about learning computer vision, your next stop should be PyImageSearch University, the most comprehensive computer vision, deep learning, and OpenCV course online today. Here you’ll learn how to successfully and confidently apply computer vision to your work, research, and projects. Join me in computer vision mastery.
Inside PyImageSearch University you'll find:
- ✓ 30+ courses on essential computer vision, deep learning, and OpenCV topics
- ✓ 30+ Certificates of Completion
- ✓ 39h 44m on-demand video
- ✓ Brand new courses released every month, ensuring you can keep up with state-of-the-art techniques
- ✓ Pre-configured Jupyter Notebooks in Google Colab
- ✓ Run all code examples in your web browser — works on Windows, macOS, and Linux (no dev environment configuration required!)
- ✓ Access to centralized code repos for all 500+ tutorials on PyImageSearch
- ✓ Easy one-click downloads for code, datasets, pre-trained models, etc.
- ✓ Access on mobile, laptop, desktop, etc.
Summary
In this blog post, I detailed how to find the extreme north, south, east, and west (x, y)-coordinates along a given contour. This method can be used on both raw contours and rotated bounding boxes.
While finding the extreme points along a contour may not seem interesting on its own, it’s actually a very useful skill to have, especially as a preprocessing step to more advanced computer vision and image processing algorithms, such as hand gesture recognition.
To learn more about hand gesture recognition, and how finding extreme points along a contour is useful in recognizing gestures, be sure to signup for the next open enrollment in the PyImageSearch Gurus course!
See you inside!
Download the Source Code and FREE 17-page Resource Guide
Enter your email address below to get a .zip of the code and a FREE 17-page Resource Guide on Computer Vision, OpenCV, and Deep Learning. Inside you'll find my hand-picked tutorials, books, courses, and libraries to help you master CV and DL!
Thank you, thank you, for this and all your blogs! They are all very helpful in our ancient brush-stroke kanji OCR projects.
Best Regards, Keith
No problem, I’m happy to help Keith! 🙂
Thanks for the useful code. In case of triangle, will it be possible to get the direction (left/right, up/down) of triangle if I have extreme points and center points.
Can you help to find the direction of arrow (exactly a triangle)?
regards
If the triangle is a perfect triangle has you described then each line of the triangle will have the same length (equilateral triangle). And if that’s the case, then the triangle is “pointing” in all three directions (or no direction, depending on how you look at it).
Thanks for the tutorial ~
if I want to find all the extreme points or fingertips how can i do it in opencv for android?
Hey Tey — I only cover OpenCV + Python on this blog post. I did not cover Android/Java.
Hi Adrian,
Great post, it works flawlessly. But can you help provide hints/reasoning for my questions?
1. What is the purpose of GaussianBlur here?
2. I’ve extended this into a live video stream and when my hand rotates back and forth there are times when there are a lot of blotches that don’t properly represent the shape’s outline.
Is this where adaptive thresholding might come into play?
Thanks,
Kevin
Also, all my searches are showing erode/dilate being called with some kind of ‘kernel’. Can you explain why you have None here?
If you supply a value of “None” then a 3×3 kernel is used by default.
1. The Gaussian blur helps reduce high frequency noise. It blurs regions of the images we are uninterested in allowing us to focus on the underlying “structure” of the image — in this case, the LCD screen and the box containing the thermostat.
2. Basic thresholding is best used under controlled lighting conditions. Adaptive thresholding can help with this, but isn’t a sure-fire solution for each problem.
Hi Adrian,
Cool stuff. I had some issues with some of my implementation. I think you can help. Here it goes the question.
I have a numpy array for a detected contour from which I have extracted extreme points in all four directions. Now I want extract 12 points. Let’ say if I start from a reference point (Extreme-top) after every 30 degree angle I want to get co-ordinates of a point. After all the traversing is done I’d be having array of 12 points which could be given to next image processing algorithm.
I hope I’m clear with my question.
Please share your thoughts on the same.
If you have the 4 extreme coordinates, compute a circle that corresponds to the area of these points (i.e., a minimum enclosing circle). Compute the (x, y)-coordinates in 30 degree increments along this circle. Then find the closest point in the contours list to this (x, y)-coordinate. This will take a bit of knowledge of trigonometry to complete, but it’s absolutely doable.
If I take hibiscus and have just 2 petals which are perpendicular to each other and need to inject the nectar part(centre part)! Can i use this technique? Or do I have a better option?
Hi Chan — it would be easier to understand your question if you could provide an example image of what you’re working with.
How can I retrieve the part of the contours that is above a certain point efficiently (Without checking the points one by one) ?
Hi Joachim — have you tried using NumPy array indexing and slicing? The vector operation of checking the coordinates would be significantly faster than trying to check the points one-by-one.
Sir i am working on hand gesture recognition in opencv using c++ but i am not able to separate my hand from other skin colour objects sir please help me on this.
Hi Adrian,
This article is really useful for me as same as your other articles. Thank you very for it.
Also I have a problem.
Can we use this method to find extreme points of a human body? I meant top of the head and bottom of the feet.
Because I want to calculate the distance (height) between both points.
Please give me a suggestion…
You would need to have the contour extraction of the human body. We were able to threshold and localize the hand in this example. Given the contours, we computed the extreme points. You would need to do the same for the human body. That said, this post on measuring object dimensions would be a better start for you.
Hi adrian
its very useful
Thank u
how i find the contour points
The blog post demonstrates how to find the contour points — via the
cv2.findContours
function. Perhaps I am misunderstanding your question?Hi Adrian!
Excellent post.
I was wondering how to get the tips of fingers? More like local maxima. But in the contour array, how to do it?
There’s a few ways to do this, but you’ll want to look up “convexity defects” as a starting point.
Hi Adrian, this is great tutorial.
I would to ask you, how do we crop just the contour and remove the outside the image contours/crop ?
Thank’s
Sorry, I’m not sure I understand. Could you elaborate or provide an example image of what you’re trying to accomplish?
Something like this https://www.quora.com/How-can-I-detect-an-object-from-static-image-and-crop-it-from-the-image-using-openCV or like this https://stackoverflow.com/questions/44383209/how-to-detect-edge-and-crop-an-image-in-python/44384019. Both of them are trying to detect edge and crop the selecting by contour, but i think its not pretty smooth yet.
Your contours algorithm detection is great, so i would like to implement it
You can use the “cv2.boundingRect” function on individual contours to compute the (x, y)-coordinates of the rectangle surrounding the object. Then use NumPy array slicing to extract the contour region and finally write it to disk using the “cv2.imwrite” function. If you’re new to working with OPenCV I would suggest reading through Practical Python and OpenCV where I teach the fundamentals — I have no doubt it will help you on this project.
Thanks Adrian.
I have a question.
I’ve notice that Contour is 3D numpy array. But why ? what problem with 2D numpy array? we know that contours is simple point in image and each point demonstrate with 2 element. But why in OpenCV contour is 3D numpy array ?
Thank you very much.
The shape and how you interpret the results of “cv2.findContours” in OpenCV is highly dependent on the flags you pass into the function (i.e., normal list of contours, hierarchy, etc.). Make sure you read the docs for the function.
Hi Adrian .. your posts have saved my life a couple of times! now you can be a hero again 🙂 ..sorry .. i am struggling with detecting objects with a lighter background ..most examples i find online generally have black as a background / backgrounds darker than the objects being detected. I am working on identification of govt issued ID cards and their pics are taken via users cell phones. Hence detecting that in a varied backdrop is becoming a huge challenge for me .. could u please check the images i loaded in the SO query here ? https://stackoverflow.com/questions/53104983/gamma-correction-for-images-with-lighter-backgrounds
afaik , for any sort of feature detection , the object of interest must appear light while the b/g should be totally dark right ? but try as i might, i am just unable to do anything with this image. Any pointers will be like manna from the heavens
There are a few ways to approach this problem but if your overall goal is to detect the government IDs regardless of background you won’t be able to rely on traditional image processing — you’ll need to use a bit of machine learning instead. I would recommend training your own custom object detector on the IDs themselves.
i want to determine the coordinate of the point residing on convex hull which is farthest from the centre of mass. How to do? please help
Do you already have the convex hull coordinates? If so, compute the center of mass (i.e., centroid) coordinates. Then compute the Euclidean distance between the centroid and all points along the hull. The point with the largest distance will be the coordinates you are looking for.
Thanks Adrian.
when i run program occure this problem:
AttributeError: module ‘imutils’ has no attribute ‘grab_contours’
Upgrade your install of the “imutils” library:
$ pip install --upgrade imutils
thanks for free tutorials, please explain me how can I find contours for continuous video using this code?
You mean apply contour detection to every frame in a video? See this tutorial as an example.
Hey excellent work I really like it. I want to detect each fingertip using your code please help me out.
How i can use the co-ordinates that found with the contours and save it to the .CSV file?
That’s not really a computer vision question, that’s a basic programming question. It’s okay if you are new to programming and Python but I would highly suggest taking some time to read basic Python tutorials, specifically ones that focus on file I/O.
Hi Adrian, your posts are very helpfull!
I am trying this code but instead of getting the contour of the object, i am getting the contour of the screen itself. For example, my image is 500×281 so the contour is drawn on this surface not on the object itself.
Is there any reason for that? What am i doing wrong?
Thank you 🙂
I tried to change the index of cv2.drawContours and put it 0 instead of -1.. Still getting same result
How is your image binarized? Is the foreground white on a black background? It sounds like you may have your binary mask inverted.
Hello Mr Adrian,
thanks for this amazing article, so fabulous.
i’ve a question, i want to comput the width of every finger! i search over internet i didn’t found anything, i was thinking if there is a relation between finger’s width and palm’s width, but it doesn’t exist!.
so, any idea how to do it?
thanks in advance.
Have you tried using this tutorial on measuring object size? You could adapt it to work with computing finger/hand size.
Yeah, i already tried it, but it doesn’t work efficiently, becaus ethe fingers is part of hand, and it just detect the hand as object!
i thought also about convexity defects, just to compute the width of the proximal phalanx, but the problem is the points of convexity defects changes everytime i change the image, they don’t stay in the same position of finger.
Hello Sir,Could you please tell me if there is a method to get the coordinates of all the points of the contours.I mean How can we get the coordinates of the all the points (locus) of the contour.I shall be grateful to you.Please help!
The contour is a list of (x, y)-points along the contour.
Hi adrain
I am working on fingerprints. I want to segment finger from a fingerphoto and then zoom-in the distal phalanx area? Is there any possible way?
A godsend! Thank you so much for this! It’s hard to figure out what functions or methods to use as a beginner and tutorials like yours help us get a firm grip on the subject matter.
Thanks, I’m glad you enjoyed it!
sir
how to get the extreme point of human body using real time web cam?
Hey,
Suppose I have a svg image,and I want all the x,y co-ordinates of the path that draws that image.How can I achieve this with open cv.
OpenCV cannot read SVG images. You would need to convert it to a PNG, JPEG, etc. and then read the image.