3

enter image description herecomplete noob at open cv and numpy here. here is the image: here is my code:

import numpy as np
import cv2

im = cv2.imread('test.jpg')
imgray = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
imgray = cv2.medianBlur(imgray, ksize=7)

ret, thresh = cv2.threshold(imgray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
_, contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)

print ("number of countours detected before filtering %d -> "%len(contours))
new = np.zeros(imgray.shape)

new = cv2.drawContours(im,contours,len(contours)-1,(0,0,255),18)

cv2.namedWindow('Display',cv2.WINDOW_NORMAL)
cv2.imshow('Display',new)
cv2.waitKey()

mask = np.zeros(imgray.shape,np.uint8)
cv2.drawContours(mask,[contours[len(contours)-1]],0,255,-1)
pixelpoints = cv2.findNonZero(mask)
cv2.imwrite("masked_image.jpg",mask)

print(len(pixelpoints))
print("type of pixelpoints is %s" %type(pixelpoints))

the length of pixelpoints is nearly 2 million since it contains all the point covered by the contours. But i only require the bordering point of that contour. How do I do it? I have tried several methods from opencv documentation but always errors with tuples and sorting operations. please...help?

I only require the border points of the contour :(

Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
gluttony47
  • 91
  • 1
  • 6
  • 1
    In [this](https://docs.opencv.org/3.1.0/d4/d73/tutorial_py_contours_begin.html) documentation, it clearly says which parameter to use to remove redundant points and get end points. I cannot believe your claim, _"I have tried several methods from opencv documentation"_ – Rick M. Apr 19 '18 at 08:16
  • Can you please upload the binary image you are using for finding the contours, along with the expected output ? – ZdaR Apr 19 '18 at 08:26
  • @RickM., i probably rubbed you wrong in some way. i apologize. i understand you are talking about changing cv2.CHAIN_APPROX_NONE to cv2.CHAIN_APPROX_SIMPLE but applying this doesnt work in a picture where there are no straight lines. I still get 2 million points. if you look at the documentation, it emphasizes on reducing line points, not arcs.i tried it. – gluttony47 Apr 19 '18 at 08:40
  • @ZdaR image added – gluttony47 Apr 19 '18 at 08:43
  • @MonirulIslam What about [this one](https://docs.opencv.org/3.4.0/dd/d49/tutorial_py_contour_features.html)? It would be helpful if you also upload the expected output. From the image you uploaded, it is _almost impossible_ to say what contour you want to extract. – Rick M. Apr 19 '18 at 08:47
  • @RickM. which functions are you referring to? contour perimeter is just a length, not a set of points. I am trying to extract the outline of the body obviously. – gluttony47 Apr 19 '18 at 08:52
  • @MonirulIslam Try using the `approxPolyDP`. You could also try using `connectedComponents` – Rick M. Apr 19 '18 at 09:05
  • @RickM.no and no. at this point i have a feeling you are just throwing whatever comes to your mind, unfortunately all of your suggestions are invalid. thanks anyway. – gluttony47 Apr 19 '18 at 09:08
  • You have uploaded the input sample image, but not your expected output. And BTW @RickM. is not throwing anything from top off his head. Maybe your question is not well defined enough, which is inhibiting the approach to a proper solution. – ZdaR Apr 19 '18 at 09:34
  • @ZdaR fair enough. the expected output is a set of pixelpoints that lies on the border of the contour. by the way, i tried to find extreme points of the contours, the statements provided for extreme points also throws an error https://docs.opencv.org/3.4.1/d1/d32/tutorial_py_contour_properties.html – gluttony47 Apr 19 '18 at 09:52

1 Answers1

3

Is this what you mean by border points of a contour?
(I used your image above to try)

The white lines you see are points that I have marked out in white against the blue drawn contours. There's a little spot at the bottom right because I think its most likely that your black background isn't really black and so when I did thresholding and a floodfill to get this,
enter image description here

there was a tiny white speck at the same spot. But if you play around with the parameters and do a more proper thresholding and floodfill it shouldn't be an issue. In openCV's drawContours function, the cnts would contain lists of contours and each contour will contain an array of points. Each point is also of type numpy.ndarray. If you want to place all points of each contour in one place so it returns you a set of points of boundary points (like the white dots outline in the image above), you might want to append them all into a list. You can try this:

#rgb is brg instead
contoured=cv2.drawContours(black, cnts, -1, (255,0,0), 3)

#list of ALL points of ALL contours
all_pixels=[]

for i in range(0, len(cnts)):
    for j in range(0,len(cnts[i])):
        all_pixels.append(cnts[i][j])

When I tried to

print(len(all_pixels))

it returned me 2139 points.

Do this if you want to mark out the points for visualization purposes (e.g. like my white points):

#contouredC is a copy of the contoured image above
contouredC[x_val, y_val]=[255,255,255]

If you want less points, just use a step function when iterating through to draw the white points out. Something like this:
enter image description here

In python, for loops are slow so I think there's better ways of replacing the nested for loops with a np.where() function or something instead. Will update this if/when I figure it out. Also, this needs better thresholding and binarization techniques. Floodfill technique referenced from: Python 2.7: Area opening and closing binary image in Python not so accurate.

Hope it helps.

Reine_Ran_
  • 662
  • 7
  • 25
  • The code does not work since you used variables that are not defined. Please rewrite your code in a way that people can actually reproduce the results instead of getting errors like this `NameError: name 'x_val' is not defined` . – Schütze Dec 03 '18 at 08:36
  • Hi, thanks for your comment. I think I didn't include the whole script because I was coding some other stuff on it as well (hence too long) and when I referred to other stackoverflow answers it seems like most of them don't show the entire code. But good suggestion though, next time I'll try to include more codes. If it helps, I can explain to you what x_val is? So x_val is what I defined myself and contouredC is the set of points that forms the contour. By setting each of these points to white ([255 255 255]), we can visualize and see them. In my most bottom pic, I used a for loop (with step) – Reine_Ran_ Jan 07 '19 at 15:05
  • to produce the spaced out white dots. If I can find this script again I'll update this answer:) – Reine_Ran_ Jan 07 '19 at 15:06
  • @Ran_Reine_ How did you find `x_val`? I need the color of each pixel in the boundary. – Rishabh Gupta Oct 23 '20 at 11:35
  • 1
    @RishabhGupta you actually set the color of each pixel in the boundary. In my example above, I set the points to white ([255,255,255]). cv2.drawContours(black, cnts, -1, (255,0,0), 3) is used to draw the contours (and hence get the points) around all contours in the image (because I set the third parameter to -1). – Reine_Ran_ Oct 25 '20 at 06:53
  • @Ran_Reine_ Okay, thanks. Do you know if we can get the pixel value at the boundary? Because when using drawContours, we can only set those pixel values. – Rishabh Gupta Oct 27 '20 at 04:29
  • @RishabhGupta I don't think you set the values when using it. drawContours actually returns you the list of contours (which are basically made up of a collection of pixel coordinates). If you look at my code above, I did not set the values - in fact, I got the values from drawContours. – Reine_Ran_ Oct 27 '20 at 10:25