First, this example only shows how to draw outlines with a simple approximation. Keep in mind that even if you draw outlines with a simple approximation, it will be rendered as having a blue outline, completely drawn around the rectangle, as seen in the left image. You cannot get the correct image by simply drawing outlines on the image. In addition, you want to compare two sets of outlines - a simplified version on the right with a full view on the left. In particular, you need to replace the cv2.CHAIN_APPROX_SIMPLE flag with cv2.CHAIN_APPROX_NONE to get a complete view. Take a look at the OpenCV document on findContours for more information: http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#findcontours
In addition, even if you draw outlines on the image, it does not display the results. To do this, you need to call cv2.imshow . However, drawing the outlines themselves will not show you the difference between the full and simplified versions. The tutorial mentions that you need to draw circles at each point in the path, so we should not use cv2.drawContours for this task. What you have to do is extract the contour points and draw circles at each point.
So create two images:
# Your code import numpy as np import cv2 im = cv2.imread('test.jpg') imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY) ret,thresh = cv2.threshold(imgray,127,255,0)
Note that the above code is for OpenCV 2. For OpenCV 3, there is an additional output for cv2.findContours , which is the first output that you can ignore in this case:
_, contours1, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) _, contours2, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
Now skip the code slowly. The first part of the code is what you provided. Now we will move on to a new one.
Step # 1 - Detecting Outlines Using Both Methods
Using the threshold image, we detect contours using both full and simple approximations. This is stored in the two lists contours1 and contours2 .
Step # 2 - Convert to 2D Matrix
The contours themselves are saved as a list of NumPy arrays. Only one outline should be detected for the provided simple image, so extract the first element of the list, then use numpy.reshape to change the 3D matrices into their 2D forms, where each row is a point (x, y) .
Step # 3 - Draw the dots as separate circles in the image
The next step is to take each point (x, y) from each set of contours and draw them on the image. We make two copies of the original image in color form, then use cv2.circle and iterate through each pair of points (x, y) for both sets of paths and fill two different images - one for each set of paths.
Now, to get the figure you see above, you can do this in two ways:
- Create an image that stores both of these results together side by side, then show this combined image.
- Use
matplotlib in combination with subplot and imshow so you can display two images in one window.
I will show you how to do this using both methods:
Method # 1
Just connect the two images side by side, then show the image after:
out = np.hstack([img1, img2])
I fold them horizontally so that they are a combined image and then show it using cv2.imshow .
Method # 2
You can use matplotlib :
import matplotlib.pyplot as plt
This should display both images in separate sub-figures in the general figure window. If you look at what I call imshow here, you will see that I exchange RGB channels because OpenCV reads images in BGR format. If you want to display images using matplotlib , you will need to cancel the channels, since the images are in RGB format (as it should be).
To answer your question in your comments, you should take the outline structure you want ( contours1 or contours2 ) and look for the points of the outline. contours is a list of all possible contours, and inside each contour there is a three-dimensional matrix, which is formed in the format N x 1 x 2 . N will be the total number of points representing the outline. I am going to remove the one-dimensional second dimension so that we can get it as an N x 2 matrix. Also, let's now use the full outline view:
points = contours1[0].reshape(-1,2)
I assume that your image has only one object, so my indexing is in contours1 with index 0. I solve the matrix so that it becomes one row vector and then changes the shape of the matrix so that it becomes N x 2 . Then we can find the minimum point:
min_x = np.argmin(points[:,0]) min_point = points[min_x,:]
np.argmin finds the location of the smallest value in the array you supply. In this case, we want to work on the x coordinate or columns. As soon as we find this location, we simply index into our array of 2D contour points and extract the contour point.