Efficient way of computing slopes of edge lines - python-2.7

I performed edge detection on images (with Python 2-7 and OpenCV 3.2) and have results like the following picture, i.e. one-pixel-wide edges not necessarily closed (can have "loose ends"), and with possible holes :
Now I would like to get the "derivative" of these edges, meaning the "slope" at each point, as in the following image :
For the moment, the only way I managed to do it is very locally. For each point of the edge (in red in next "zoomed" picture), I create a circle around it (in pink), mask the circle with the edge to get the red point's neighbors, then compute the slope of these two neighbors.
However, it can be quite messy if edges have holes (which they often do) or are close to other edges (which they often are) and masking all the points is pretty computationally intensive, so I wonder if there is a better way.
My first idea was spline interpolation, but you need to give as input an ordered list of points, which you can't have for a given edge unless you use a pixel neighbor tracking algorithm which can also get quite messy in case of not-that-good edges.
I also thought of findContours but it needs closed edges or else it yields the contour of a one-pixel-wide edge, i.e. two lines on both side of the edges, started at an arbitrary location on the edge, in short it's a mess.
Is there a cleaner and more efficient way than my actual method to achieve what I want ? Does OpenCV have any resources or is its job done after edge detection (I think the latter is more probable !) ?
P.S. : "I don't think there is a better way" is an answer I'm ready to accept !

So, if I understood everything correctly, what you need is an ordered list of your points free from holes, because after that it seems you know how to proceed to obtain your result. So, you should concentrate in getting an ordered gapless list.
FindContours does output an ordered list, but probably not in the order you need. It groups connected pixels with a TOP-DOWN / LEFT-RIGHT priority. So, it swipes each row sequentially, when it hits a white pixel, it finds the first contour. So, in your image, the first contour it finds is actually the one on the right, since it has the closest to 0 Y value.
In the case of this particular image, if you rotate it 90 degrees you'll realize that it will actually order your contours and points in the way you need. But will this always be the case? Only you can tell. If there is a pre-process method to apply to your images that will guarantee that findContours will order your pixels in the correct way, the rest will be easy. If not, I suggest you create your own pixel-connectivity algorithm that will work as you need it to, since all your problems depends on getting an ordered list.
Once you have the ordered list, just interpolate the missing pixels.
If you have an ordered set of pixels, "closing the gaps" is easy, since you just need to find the gaps and interpolate between them as an approximation that probably wont hurt your algorithm.

Related

Clustering Points Algorithm

I've applied three different methods of getting sets of points as follows.
Every method produces a vector of Points. Each method is in a different color, red, blue, and green.
Here is the combined image, overlaying all 3 of the sets of points
As you can see in the combined image there are spots in which all three sets "agree" on (i.e are generally in the exact same spot). I would like to find these particular spots and combine them into a single coordinate. I'm not sure where to start with approaching this problem. I've looked into K-means clustering, but to me it seems the problem is that K-means will cluster all the points and take the average with surrounding points, shifting the cluster center from the original position. I could loop through all the points in all the vectors that store the points, but as these images get larger with more points, it becomes very costly and inefficient.
Does anybody have any tips on how to approach this problem? I've been using OpenCV with C++.
Notionally, what you want to do is consider the complete tripartite graph on the three sets of points with edges weighted by distance. Then select edges in order of weight until a triangle appears; call those points a corresponding set, choose (say) their centroid to represent them, and remove them from the graph. Stop when the edge length exceeds some tolerance.
The mathematical justification for this approach is that it is independent of point ordering (except in the unlikely case of problematic ties in distances between points).
The practical implementation of this algorithm (for a significant number of points) involves a search data structure that can quickly find nearby points (not just the nearest): bins of the threshold size, a quad trie, or a k-d tree would work. Probably you would create one for each point set and use the other sets’ points as query points.

Find the Peaks of contour in Python-OpenCV

I have got a binary image/contour containing four human beings, and I want to detect/count all humans. Since there are occlusions, so I think it is best to get the head/maxima in the contour of all the humans. In that case human can be counted.
I am able to get the global maxima\topmost point (in terms of calculus language), but I want to get all the local maximas
The code for finding the topmost point is as suggested by Adrian in his blogpost i.e.:
topmost = tuple(biggest_contour[biggest_contour[:,:,1].argmin()][0])
Can anyone please suggest how to get all the local maximas, instead of just topmost location?
Here is the sample of my Image:
The definition of "local maximum" can be tricky to pin down, but if you start with a simple method you'll develop an intuition to look further. Even if there are methods available on the web to do this work for you, it's worth implementing a few basic techniques yourself before you go googling.
One simple method I've used in the path goes something like this:
Find the contours as arrays/lists/containers of (x,y) coordinates.
At each element N (a pixel) in the list, get the pixels at N - D and N + D; that is the pixels D ahead of the current pixel and D behind the current pixel
Calculate the point-to-point distance
Calculate the distance along the contour from N-D to N+D
Calculate (distanceAlongContour)/(point-to-point distance)
...
There are numerous other ways to do this, but this is quick to implement from scratch, and I think a reasonable starting point: Compare the "geodesic" distance and the Euclidean distance.
A few other possibilities:
Do a bunch of curve fits to chunks of pixels from the contour. (Lots of details to investigate here.)
Use Ramer-Puecker-Douglas to render the outlines as polygons, then choose parameters to ensure those polygons are appropriately simplified. (Second time I've mentioned R-P-D today; it's handy.) Check for vertices with angles that deviate much from 180 degrees.
Try a corner detector. Crude, but easy to implement.
Implement an edge follower that moves from one pixel to the next in the contour list, and calculate some kind of "inertia" as the pixel shifts direction. This wouldn't be useful on a pixel-by-pixel basis, but you could compare, say, pixels N-1,N,N+1 to pixels N+1,N+2,N+3. Or just calculate the angle between them.

Finding the middle point of 2 parallel contours

Couldn't find this with search but I'm not sure how to describe this anyway, which is one reason the problems been so hard to find the right approach for. Sorry if its already been asked or if the problem description is to vague.
The problem: I am detecting the contours of a curving, painted physical path of consistent width from an image using opencv's findcontours. I need to map a vector of points in the middle of those 2 edges along the length of the path so as to trace the painted path with a single vector.
I was wondering if there is a way to find points within a certain pixel distance in the imagespace, in various other contours. I can iterate through them all, and find the closest ones that way, but thats time intensive.
If there was I could search the other contour vectors for points about the right width away and use the 2 points to estimate a mid point and add that to the growing middle vector.
Or if theres a better approach to converting the detected path into a single, workable vector that would work to.
Sounds like you're looking for the "skeleton" - it helps if you know the terms.
If you can transform the image into a black&white image that still shows the path, it's trivial: iterate the erosion procedure until no more points are eroded.
Another approach is to realize that the operation is expensive once, but once you've found one pair of points, the next pair is easily found in the respective 3x3 neighborhoods.

OpenCV 'Almost' Closed contours

I'm trying to extract the cube from the image (looks like a square...). I've used canny and dilate to get the edges and remove the noise.
I'm not even sure if it is possible to get the square out in a robust way.
Advice appreciated!
Thanks.
It's not excessively hard.
Sort all edges by direction. Look for a pair of edges in one direction with another pair 90 degrees rotated. Check for rough proximity. If so, they probably form a rectangle. Check the edge distances to pick the squares from the rectangles, and to discard small squares. Check if you have sufficiently large parts of the edge to be convinced the entire edge must exist. An edge might even be broken in 2. Check if the 4 edges now found delimit an area that is sufficiently uniform.
The last bit is a bit tricky. That's domain knowlegde. Could there be other objects inside the square, and could they touch or overlap the edges of the square?
You can utilize color information and kmeans clustering as explained in the link.
As long as target object color differs from the background, the pixels of the square object can be detected accurately.

How to detect points which are drastically different than their neighbours

I'm doing some image processing, and am trying to keep track of points similar to those circled below, a very dark spot of a couple of pixels diameter, with all neighbouring pixels being bright. I'm sure there are algorithms and methods which are designed for this, but I just don't know what they are. I don't think edge detection would work, as I only want the small spots. I've read a little about morphological operators, could these be a suitable approach?
Thanks
Loop over your each pixel in your image. When you are done considering a pixel, mark it as "used" (change it to some sentinel value, or keep this data in a separate array parallel to the image).
When you come across a dark pixel, perform a flood-fill on it, marking all those pixels as "used", and keep track of how many pixels were filled in. During the flood-fill, make sure that if the pixel you're considering isn't dark, that it's sufficiently bright.
After the flood-fill, you'll know the size of the dark area you filled in, and whether the border of the fill was exclusively bright pixels. Now, continue the original loop, skipping "used" pixels.
How about some kind of median filtering? Sample values from 3*3 grid (or some other suitable size) around the pixel and set the value of pixel to median of those 9 pixels.
Then if most of the neighbours are bright the pixel becomes bright etc.
Edit: After some thinking, I realized that this will not detect the outliers, it will remove them. So this is not the solution original poster was asking.
Are you sure that you don't want to do an edge detection-like approach? It seems like a comparing the current pixel to the average value of the neighborhood pixels would do the trick. (I would evaluate various neighborhood sizes to be sure.)
Personally I like this corner detection algorithms manual.
Also you can workout naive corner detection algorithm by exploiting idea that isolated pixel is such pixel through which intensity changes drastically in every direction. It is just a starting idea to begin from and move on further to better algorithms.
I can think of these methods that might work with some tweaking of parameters:
Adaptive thresholds
Morphological operations
Corner detection
I'm actually going to suggest simple template matching for this, if all your features are of roughly the same size.
Just copy paste the pixels of one (or a few features) to create few templates, and then use Normalized Cross Correlation or any other score that OpenCV provides in its template matching routines to find similar regions. In the result, detect all the maximal peaks of the response (OpenCV has a function for this too), and those are your feature coordinates.
Blur (3x3) a copy of your image then diff your original image. The pixels with the highest values are the ones that are most different from their neighbors. This could be used as an edge detection algorithm but points are like super-edges so set your threshold higher.
what a single off pixel looks like:
(assume surrounding pixels are all 1)
original blurred diff
1,1,1 8/9,8/9,8/9 1/9,1/9,1/9
1,0,1 8/9,8/9,8/9 1/9,8/9,1/9
1,1,1 8/9,8/9,8/9 1/9,1/9,1/9
what an edge looks like:
(assume surrounding pixels are the same as their closest neighbor)
original blurred diff
1,0,0 6/9,3/9,0/9 3/9,3/9,0/9
1,0,0 6/9,3/9,0/9 3/9,3/9,0/9
1,0,0 6/9,3/9,0/9 3/9,3/9,0/9
Its been a few years since i did any image processing. But I would probably start by converting to a binary representation. It doesn't seem like you're overly interested in the grey middle values, just the very dark/very light regions, so get rid of all the grey. At that point, various morphological operations can accentuate the points you're interested in. Opening and Closing are pretty easy to implement, and can yield pretty nice results, leaving you with a field of black everywhere except the points you're interested in.
Have you tried extracting connected components using cvContours? First thresholding the image (using Otsu's method say) and then extracting each contour. Since the spots you wish to track are (from what I see in your image) somewhat isolated from neighborhood they will some up as separate contours. Now if we compute the area of the Bounding Rectangle of each contour and filter out the larger ones we'd be left with only small dots separate from dark neighbors.
As suggested earlier a bit of Morphological tinkering before the contour separation should yield good results.