Adapting scanline fill to detect discrete objects - python

I'm trying to detect contiguous areas of close-enough colors in Python. I independently stumbled across the 8-way recursive flood fill algorithm (terminating when the Euclidian distance between the found and desired RGB colors exceeds a threshold), which works great at small scale, but causes a stack overflow on a 2 megapixel image.
Stack Overflow and Wikipedia point to scanline fill as the answer, but every explanation I've found is either in C++ or about filling a polygon with known vertices. Can someone point me to a good pseudocode explanation for a situation analogous to recursive flood fill?
I'm hitting a wall on researching image segmentation due to a lack of formal mathematics (I'm in high school.) If there is a plain-English explanation of K-Means or something like it, that'd be great too. OpenCV looked promising but it appears all I get is a color-flattened image; all I care about is a list of pixels in the object at x,y.

The idea of scanline flood fill is the following
you are given the initial point (seed) (x, y)
go left as far as possible until the pixel (x-1, y) is not to be filled or you get to x=0
the x you reached will be the start of scanline; keep two flags "look for caverns above" and "look for caverns below", both initialized at true
this is the begin of scanline loop. Check the pixel above (x, y-1) if it's to be filled and now considering the look-above flag and the pixel there are 4 cases:
if "look above" is true and the pixel is to be filled then you found a "new cavern", store (x, y-1) in the "to-do-list" and mark "look above" as false
if "look above" is false and the pixel is NOT to be filled then the current cavern is complete and you need to look for another one, so just mark "look_above" as true
in other cases (look above is true and pixel is not to be filled or look above is false and pixel is to be filled) you just do nothing
Repeat the same reasoning with the "look below" flag and the color of pixel (x, y+1)
paint the pixel (x, y) and move to (x+1, y), repeating from (5) unless the pixel you move to is not going to be painted.
if there is anything in the "to-do list" pick it out and go back at (1) using the coordinates you found in the to-do list as (x, y)
This is the version for a 4-connected flood fill. For an 8-connected fill you also need to check for caverns at (x-1, y-1) and (x-1, y+1) when starting the scanline and you need to check for caverns (x+1, y-1) and (x+1, y+1) at scanline end (if the corresponding flags are true).
When moving on the scanline what you want to do is adding the green points in the picture to your to-do list:
Note that the number of seeds will not be "minimal" (for example the first two "above seeds" in the example will end up in the same cavern and therefore only one of them is really needed). Still the amount of stack needed to store them will be normally much smaller than the amount needed for a pixel-by-pixel recursive approach.
Another possible approach to limit the amount of memory needed is using a frontier painting algorithm:
You put the initial seed (x, y) in the current_active list and paint it
Initialize a next_active list to empty
for every pixel in the current_active list check for neighbors that need to be painted: when you find one paint it and add it to the next_active list
when you're done set current_active list to next_active list and repeat from 2 unless the list was empty
You can see an example of the two algorithms in action in this video.

Related

How can I detect circles in python with a set of (x, y) points

I have a list of (x,y) points that constitue several circles with different centers, they all have the same diameter (which is known).
I need to detect the number of circles in total (not necessary to define their parameters). Is there a simple way to do that in python? (preferably without openCV)
If all circles have the same size and they do not intersect, you can just scan the picture line-by-line, pixel-by-pixel.
When you meet a pixel of circle color, apply flood-fill algorithm from this point and mark all connected pixels of the same color with the same integer value (1 for the first circle and so on).
After all the last value is number of objects.
Aslo you can use connected-component labelling algorithm

Is there a simple way to extract the (x, y) pixel positions from outside curves in an image using Python

I have an image of a tube as shown below, and I need to extract the (x, y) pixel positions for all 4 edges in SEPARATE lists, meaning one list for the left edge pixels, one list for the right edge pixels, etc.
If I just needed to find all the edges in one list that would be trivial by looping over the image, however, I can't find an appropriate method for splitting up this data collection. Thank you.

How do I fit rectangles to an image in python and obtain their coordinates

I'm looking for a way to split a number of images into proper rectangles. These rectangles are ideally shaped such that each of them take on the largest possible size without containing a lot of white.
So let's say that we have the following image
I would like to get an output such as this:
Note the overlapping rectangles, the hole and the non axis aligned rectangle, all of these are likely scenario's I have to deal with.
I'm aiming to get the coordinates describing the corner pieces of the rectangles so something like
[[(73,13),(269,13),(269,47)(73,47)],
[(73,13),(73,210),(109,210),(109,13)]
...]
In order to do this I have already looked at the cv2.findContours but I couldn't get it to work with overlapping rectangles (though I could use the hierarchy model to deal with holes as that causes the contours to be merged into one.
Note that although not shown holes can be nested.
A algorithm that works roughly as follow should be able to give you the result you seek.
Get all the corner points in the image.
Randomly select 3 points to create a rectangle
Count the ratio of yellow pixels within the rectangle, accept if the ratio satisfy a threshold.
Repeat 2 to 4 until :
a) every single combination of point is complete or
b) all yellow pixel are accounted for or
c) after n number of iteration
The difficult part of this algorithm lies in step 2, creating rectangle from 3 points.
If all the rectangles were right angle, you can simply find the minimum x and y to correspond for topLeft corner and maximum x and y to correspond for bottomRight corner of your new rectangle.
But since you have off axis rectangle, you will need to check if the two vector created from the 3 points have a 90 degree angle between them before generating the rectangle.

Randomly placing N circles in rectangle without overlapping

I want to place N circles with given, common radius in the rectangle of given size, such that circles are not overlapping in Python. My current solutions are:
1) to create a set of every point in the space and remove from it points that will cause overlapping before generating next circle (but it's slow when the rectangle is big).
2) to draw the center of balls from the set of not-overlapping points (e.g. every 2r + const) (but the positions are not random enough here).
Do you have other, more efficient ideas?
so the most efficient packing in 2D is hexagonal packing and you can just hard code your program to give that packing for circles
read more about it here : https://en.wikipedia.org/wiki/Circle_packing

find closest clusters of colors in a numpy array from an image file

Current state
I have a numpy array of shape (900, 1800, 3) that has been made from an image file.
That's one array element per pixel: 900 px high, 1800 px wide, and 3 channels (R, G, B) per pixel represented in the array.
There are only a small number (3-20) unique RGB colors in the images being parsed, so there are only very few different RGB value combinations represented in the array.
Goal
Identify the smallest circular areas in the image that contains n number of unique colors, where n will always be less than or equal to the number of unique colors in the image.
Return top y (by count or pct) of the smallest areas.
A 'result' could simply be the x,y value of the center pixel of an identified circular area and its radius.
I do plan to draw a circle around each area, but this question is about the best approach for first identifying the top smallest areas.
The Catch/Caveat
The images are actually flattened projections of spheres. That means that a pixel at the right edge of the image is actually adjacent to a pixel on the left edge, and similarly for top and bottom pixels. The solution must account for this as it is parsing pixels to identify closest pixels with other colors. EDIT: this part may be answered in comments below
The Question
My initial approach is to simply parse pixel by pixel and brute force the problem with handrolled x/y coordinate math: take a pixel, work outwards until we hit n colors, score that pixel for how many steps outward it took, next pixel. Keep a top y dict that gets re-evaluated after each pixel, adding any pixels that make top y, and dumping any that get pushed out. Return that dict as the output.
I know that many python libs like scipy, scikit-image, and maybe others like to work with images as numpy arrays. I'm sure there is a method/approach that is smarter and leverages a library or some kind of clustering algo instead of brute forcing it, but I'm not familiar enough with the space to know intuitively what methods and libs to consider. The question: What is the pseudocode for a good method/lib to do this the right way?

Categories

Resources