How to display image outlines using OpenCV Python?

I followed this guide from the official documentation . I run their 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) contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(img, contours, -1, (0,255,0), 3) 

This is normal: no errors, but nothing is displayed. I want to show the result that they got when they showed it in the picture:

enter image description here

How can I display the result of such counters (only left or right)? I know that I should use cv2.imshow(something) , but how in this particular case?

+5
source share
3 answers

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) ## Step #1 - Detect contours using both methods on the same image contours1, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE) contours2, _ = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) ### Step #2 - Reshape to 2D matrices contours1 = contours1[0].reshape(-1,2) contours2 = contours2[0].reshape(-1,2) ### Step #3 - Draw the points as individual circles in the image img1 = im.copy() img2 = im.copy() for (x, y) in contours1: cv2.circle(img1, (x, y), 1, (255, 0, 0), 3) for (x, y) in contours2: cv2.circle(img2, (x, y), 1, (255, 0, 0), 3) 

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]) # Now show the image cv2.imshow('Output', out) cv2.waitKey(0) cv2.destroyAllWindows() 

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 # Spawn a new figure plt.figure() # Show the first image on the left column plt.subplot(1,2,1) plt.imshow(img1[:,:,::-1]) # Turn off axis numbering plt.axis('off') # Show the second image on the right column plt.subplot(1,2,2) plt.imshow(img2[:,:,::-1]) # Turn off the axis numbering plt.axis('off') # Show the figure plt.show() 

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.

+10
source

Add these 2 lines at the end:

 cv2.imshow("title", im) cv2.waitKey() 

Also, keep in mind that you have img instead of im in your last line.

+3
source

You should add cv2.imshow ("Title", img) at the end of your code. It should look like this:

 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) contours, hierarchy = cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE) cv2.drawContours(im, contours, -1, (0,255,0), 3) cv2.imshow("title", im) cv2.waitKey() 
+1
source

Source: https://habr.com/ru/post/1214005/


All Articles