Building tree/graph from image points - c++

I start to describe my problem with this picture:
In the picture, we can see some points (black dots). What I want to do is to first store all the points and then find the node points and the tip points (red dots).
What is more, I need to check if these red points can be connected by straight lines (along the black points) to find angles between the red lines.
I don't know if I explained it clearly enough, but what I figured is that I should implement a tree/graph and than use some path finding to check if the red points are connected?
Basically, I started with something like:
class Point {
public:
int x;
int y;
vector<Point> neighbors;
Point(void);
Point(int x, int y);
}
vector<Point> allPoints;
Where I store all the points in allPoints vector. Than for each Point, I check all his neighbors ([x+1,y], [x-1,y], [x+1,y+1], [x-1, y+1], ...) and store them in neighbors vector for that Point.
Then, by the size of the neighbors vector, I determine if the Point is a node (3 or more neighbors), a tip (1 neighbor), or just some basic point (2 neighbors).
And here comes the part, where I have no idea how to implement some path finding (to check whether there is a way for example from a tip point to the closest node point).
What is more, I have no idea if my "tree" representation is good (probably is not). So if anyone would help me to achieve what I want, it would be great.
P.S. I'm writing in C++ (and OpenCV) and VS2010.
Edit:
This is how it looks like in a real program (red lines are drown by me in paint, but this is what i want to achieve):

I'm not sure if that post should be an answer or edit, because i have no idea if anyone would notice that I added something, co i decided to post it as an answer. Sorry if its wrong.
About a problem. I did some codding, I belive I know how to do what I wanted, but another problem came out ;d.
Well lets start with picture:
To see where is a problem, you have to zoom above picture, about 400%, and take a look inside green rectangles. Image "skeleton" are my basic lines, and image "temp", are my output tips and nodes. As you can see, tips are fine (violet rectangles) (points with 1 neighbor) but unfortunately there are lines where pixel have 3 or more neighbors and they are not nodes (right green rectangle on "skeleton" is the line which have no nodes.. and green rectangle on "temp" are my false nodes .. marked because of the specific pixels positions).When You super zoom it, You are going to notice that I marked pixels with colors to make it more clear.
So the problem is that sometimes both nodes and "branches" have more than 2 neighbors which leads me to a problem "how to find a difference between them". All i need are nodes (small green rectangle on "skeleton" image - when you zoom you will see that there are 2 pixels that could be a node, but this is not important as long as they are so close to each other).
Any help?
If you need code, just tell and I will edit & paste it.
Edit:
I did something!
I found a way to filter redundant pixels from the lines. But that made some of my skeleton lines to disconnect, and that is not good, because some nodes are considered as "tips" now.
Just look at the picture:
Small dots are "good" nodes, but dots inside red rectangles, are nodes which got disconnected (zoom in to see that), and are now considered as tips.
How did I filtered the pixels? Here is the code:
void SKEL::clearPixels(cv::Mat& input)
{
uchar* data = (uchar *)input.data;
for (int i = 1 ; i < input.rows-1 ; i++)
{
for (int j = 1 ; j < input.cols-1 ; j++)
{
if (input.at<uchar>(i,j) == 255) //if its part of skeleton
{
if (input.at<uchar>(i-1,j+1) == 255)
{
if (input.at<uchar>(i,j+1) == 255) data[i*input.step+(j+1)*input.channels()] = 0;
if (input.at<uchar>(i-1,j) == 255) data[(i-1)*input.step+(j)*input.channels()] = 0;
}
if (input.at<uchar>(i+1,j+1) == 255)
{
if (input.at<uchar>(i,j+1) == 255) data[i*input.step+(j+1)*input.channels()] = 0;
if (input.at<uchar>(i+1,j) == 255) data[(i+1)*input.step+(j)*input.channels()] = 0;
}
if (input.at<uchar>(i-1,j-1) == 255)
{
if (input.at<uchar>(i,j-1) == 255) data[i*input.step+(j-1)*input.channels()] = 0;
if (input.at<uchar>(i-1,j) == 255) data[(i-1)*input.step+(j)*input.channels()] = 0;
}
if (input.at<uchar>(i+1,j-1) == 255)
{
if (input.at<uchar>(i,j-1) == 255) data[i*input.step+(j-1)*input.channels()] = 0;
if (input.at<uchar>(i+1,j) == 255) data[(i+1)*input.step+(j)*input.channels()] = 0;
}
}
}
}
}
For each pixel I checked if it has (x+1,y-1) (x+1,y+1) (x-1,y+1) (x-1, y-1) neighbor and if it does , i checked if there are neighbors just next to that neighbor and removed them. It was the only idea that I had , and its quite slow, but for now , nothing better comes to my mind.
So now, my main problem is. How to reconnect broken nodes ? ..

This is late but I got something working and put it in a github repo: pixeltree (it's too big to just paste the whole thing here). I work in python rather than c++ but I hope the idea will help if you or someone else needs it. Disclaimer - there is probably some accepted method of doing this but I didn't find it.
Approach
Separate the image into as many regions as you want (e.g. black/white)
For each region, choose any pixel
Build a full tree from that pixel that covers every pixel in the region
Cut back the non-meaningful branches of the tree until it is a minimal tree
Details
The hard part is the last step of cutting back the tree. This is the approach I used:
For each region again, choose a root at a "distant" pixel (technically, one end of the longest path in the graph).
For each leaf in that tree, perform a breadth-first-search until encountering another branch. Eliminate the branch of this leaf if its height is less than the encountered pixel.
Examples
Embarrassingly large function that does most of the work:
def treeify(image, target_families=None):
# family map is used everywhere to identify pixels, regions, etc.
family_map = _make_family_map(image)
target_families = target_families or np.unique(family_map)
trees = list()
rows, cols = family_map.shape
remaining_points = set((r, c) for r in range(rows) for c in range(cols)
if family_map[r, c] in target_families)
# the "tree" here is actually just a non-cyclic undirected graph which could
# be rooted anywhere. The basic graph is done with each pixel pointing to some neighbors (edges)
edges = np.empty_like(family_map, dtype=object)
edges.flat = [set() for _ in edges.flat]
# continue until all regions within the graph are handled
while remaining_points:
# grow a tree from any remaining point until complete
start_p = remaining_points.pop()
family = family_map[start_p]
tree = {'family': family,
'any_point': start_p}
trees.append(tree)
q = [(None, start_p)]
while q:
# pushright + popleft --> breadth first expansion
# random within left part of q - roughly BFS with less pattern
source, p = q.pop(random.randrange(0, max(1, len(q)//2)))
try:
remaining_points.remove(p)
except KeyError:
pass # tree start point is always already gone
# send qualifying neighbors for expansion
q_points = tuple(qp for sp, qp in q)
expansion_points = [n for n in _neighbors(p, 'all', family_map)
if all((n != source,
n in remaining_points,
n not in q_points,
family_map[n] == family))]
expansion_pairs = [(p, n) for n in expansion_points]
q.extend(expansion_pairs)
# document all edges for this point
if source is None:
all_connections = list(expansion_points)
else:
all_connections = expansion_points + [source]
edges[p].update(all_connections)
# prune all but "best" branches within each area
for tree in trees:
family = tree['family']
# root graph at one end of the longest path in the graph
distant_point = _most_distant_node(tree['any_point'], edges)
# for choosing best paths: document the height of every pixel
heights = _heights(distant_point, edges)
remaining_leaves = set(_leaves(distant_point, edges))
# repeatedly look for a leaf and decide to keep it or prune its branch
# stop when no leaves are pruned
while remaining_leaves:
leaf = remaining_leaves.pop()
# identify any degenerate path to next branching pixel
# this path is ignored when testing for nearby branches
ignore = set(_identify_degenerate_branch(leaf, edges))
# BFS expansion to find nearby other branches
expansion_q = deque()
expansion_q.append(leaf)
while expansion_q:
p = expansion_q.popleft() # pushright + popleft for BFS
ignore.add(p)
# decide what to do with each neighbor
for n in _neighbors(p, 'sides', family_map):
if n in ignore:
continue # already decided to ignore this point
elif n in expansion_q:
continue # already slated for expansion testing
elif family_map[n] != family:
ignore.add(n) # ignore other families
continue
elif len(edges[n]) == 0:
expansion_q.append(n) # expand into empty spaces
elif _disqualified(leaf, n, edges, heights):
_prune_branch_of_leaf(leaf, edges, heights)
expansion_q.clear() # this leaf is done. stop looking
break
else:
expansion_q.append(n)
return trees, edges

Related

OpenCV estimateRigidTransform()

After reading several posts about getting the 2D transformation of 2D points from one image to another, estimateRigidTransform() seems to be the recommendation. I'm trying to use it. I modified the source code (to change the RANSAC parameters, because its hardcoded, and the hardcoded parameters are not very good)(the source code for this function is in lkpyramid.cpp). I have read up on how RANSAC works, and am trying to understand the steps in estimateRigidTransform().
// choose random 3 non-complanar points from A & B
...
// additional check for non-complanar vectors
a[0] = pA[idx[0]];
a[1] = pA[idx[1]];
a[2] = pA[idx[2]];
b[0] = pB[idx[0]];
b[1] = pB[idx[1]];
b[2] = pB[idx[2]];
double dax1 = a[1].x - a[0].x, day1 = a[1].y - a[0].y;
double dax2 = a[2].x - a[0].x, day2 = a[2].y - a[0].y;
double dbx1 = b[1].x - b[0].x, dby1 = b[1].y - b[0].y;
double dbx2 = b[2].x - b[0].x, dby2 = b[2].y - b[0].y;
const double eps = 0.01;
if( fabs(dax1*day2 - day1*dax2) < eps*std::sqrt(dax1*dax1+day1*day1)*std::sqrt(dax2*dax2+day2*day2) ||
fabs(dbx1*dby2 - dby1*dbx2) < eps*std::sqrt(dbx1*dbx1+dby1*dby1)*std::sqrt(dbx2*dbx2+dby2*dby2) )
continue;
Is it a typo that it uses non-coplanar vectors? I mean the 2D points are all on the same plane right?
My second question is what is that if condition doing? I know that the left hand side (gives the area of triangle times 2) would be zero or near zero if the points are collinear, and the right hand side is the multiplication of the lengths of 2 sides of the triangle.
Collinearity is preserved in affine transformations (such as the one you are probably estimating), but this transformations also calculate also changes in rotations in point of view (as if you rotated the object in a 3d world). However, these points will be collinear as well, so for the algorithm it may have not a unique solution. Look at the pictures:
imagine selecting 3 center points of each black square in the first row in the first image. Then map it to the same centers in the next image. It may generate a mapping to that solution, but also a mapping to a zoom version of the first one. The same may happen with the third one, just that this time may map to a zoom out version of the first one (without any other change). However if the points are not collinear, for example, 3 corner squares centers, it will find a unique mapping.
I hope this helps you to clarify your doubts. If not, leave a comment

finding shortest path given distance transform image

I am given a distance transform (below) and I need to write a program that finds the shortest path going from point A(140,200) to point B(725,1095) while making sure I am at least ten pixels away from any obstacle
distance_transform_given
(the above image is the distance transform of map)
This is what I have done so far:
I started off at the initial point and evaluated the grayscale intensity of every point around it. ( 8 neighboring points that is)
Then I moved to the point with the highest grayscale intensity of the 8 neighboring points.
Then I repeated this process but I get random turns and not the shortest path.
please do help me out
code of what I have done so far :
def find_max_neigh_location(np,img):
maxi = 0
x0=0
y0=0
for i in range(len(np)):
if img[np[i][0]][np[i][1]][0] >maxi:
maxi = img[np[i][0]][np[i][1]][0]
x0 = np[i][0]
y0 = np[i][1]
return x0,y0
-----------------------------------------------------------------
def check_if_extremes(x,y):
if(x==1099 and y==1174):return 1
elif(y==1174 and x!=1099):return 2
elif(x==1099 and y!=1174):return 3
else:return 0
--------------------------------------------------------
def find_highest_neighbour(img,x,y,visted_points):
val = check_if_extremes(x,y)
if val==1:
neigh_points = [(x-1,y),(x-1,y-1),(x,y-1)]
np = list(set(neigh_points)-set(visited_points))
x0,y0 = find_max_neigh_location(np,img)
elif val==2:
neigh_points = [(x-1,y),(x-1,y-1),(x,y-1),(x+1,y-1),(x+1,y)]
np = list(set(neigh_points)-set(visited_points))
x0,y0 = find_max_neigh_location(np,img)
elif val==3:
neigh_points = [(x-1,y),(x-1,y-1),(x,y-1),(x,y+1),(x-1,y+1)]
np = list(set(neigh_points)-set(visited_points))
x0,y0 = find_max_neigh_location(np,img)
elif val==0:
neigh_points = [(x-1,y),(x-1,y-1),(x,y-1),(x,y+1),(x+1,y),(x+1,y+1),(x,y+1),(x-1,y+1)]
np = list(set(neigh_points)-set(visited_points))
x0,y0 = find_max_neigh_location(np,img)
for pt in neigh_points:
visited_points.append(pt)
return x0,y0,visited_points
---------------------------------------------------------
def check_if_neighbour_is_final_pt(img,x,y):
l = [(x-1,y), (x+1,y),(x,y-1),(x,y+1),(x-1,y-1),(x+1,y+1),(x-1,y+1),(x+1,y-1)]
if (725,1095) in l:
return True
else:
return False
--------------------------------------------------------------
x=140
y=200
pos=[]
count = 0
visited_points = [(x,y)]
keyword = True
while keyword:
val = check_if_neighbour_is_final_pt(img,x,y)
if val == True:
keyword = False
if val == False:
count=count+1
x,y,visited_points = find_highest_neighbour(img,x,y,visited_points)
img[x][y] = [255,0,0]
cv2.imwrite("img\distance_transform_result__"+str(count)+".png",img)
As you did not comment your code at all I won't read through your code.
I'll stick to what you described as your approach.
The fact that you start at point A and move to the brightest point A's neigbourhood shows that you don't know what distance transform does or what it is you see in your distance map... Never start coding if you don't know what you're dealing with.
Distance transform transforms a binary image into an image where each pixel's value is the minimum distance of the input image's foreground pixel to the background.
Dark pixels mean close to background (obstacles in your problem) and bright pixels are further away.
So moving to the brightest pixel nearby will only lead you away from the obstacles but never to your target point.
First restriction:
Never get closer to an obstacle than 10 pixels!
This means, every pixel that is closer to the obstacle (darker than 10) cannot be part of your path. So apply a global threshold of 10 to your distance map.
Now every white pixel can be used for your path to B.
The rest ist an optimization problem. There is plenty of literature on shortest path algorithms online. I'll leave that up to you...

Return progress status when drawing a large NetworkX graph

I have a large graph that I'm drawing that is taking a long time to
process.
Is it possible to return a status, current_node, or percentage of the current status of the drawing?
I'm not looking to incrementally draw the network as all I'm doing it is saving it to a high dpi image.
Here's an example of the code I'm using:
path = nx.shortest_path(G, source=u'1234', target=u'98765')
path_edges = zip(path, path[1:])
pos = nx.spring_layout(G)
nx.draw_networkx_nodes(G,pos,nodelist=path,node_color='r')
nx.draw_networkx_edges(G,pos,edgelist=path_edges,edge_color='r',width=10)
plt.axis('equal')
plt.savefig('prototype_map.png', dpi=1000)
plt.show()
I believe the only way to do it is to accommodate the source code of draw function to print something saying 10%, 20% complete.... But when I checked the source code of draw_networkx_nodes & draw_networkx, I realized that it is not a straight forward task as the draw function stores the positions (nodes and edges) in a numpy array, send it to the ax.scatter function of matplotlib (sourcecode) which is a bit hard to manipulate without messing something up. The only thing I can think of is to change:
xy = numpy.asarray([pos[v] for v in nodelist]) # In draw_networkx_nodes function
To
xy = []
count = 0
for v in nodelist:
xy.append(pos[v])
count +=1
if (count == len(nodelist)):
print '50% of nodes completed'
print '100% of nodes completed'
xy = numpy.asarray(xy)
Similarly when draw_network_edges is called, to indicate progress in edges drawing. I am not sure how far from truth this will be because I do not know how much time is spent in the ax.scatter function. I also, looked in the source code of the scatter function but I could not pin point a loop or something to print an indication that some progress has been done.
Some layout functions accept pos argument to conduct incremental work. We can use this fact to split the computation into chunks and draw a progress bar using tqdm
def plot_graph(g, iterations=50, pos=None, k_numerator=None, figsize=(10, 10)):
if k_numerator is None:
k = None
else:
k = k_numerator / np.sqrt(g.number_of_nodes())
with tqdm(total=iterations) as pbar:
step = 5
iterations_done = 0
while iterations_done < iterations:
pos = nx.layout.fruchterman_reingold_layout(
g, iterations=step, pos=pos, k=k
)
iterations_done += step
pbar.update(step)
fig = plt.figure(figsize=figsize, dpi=120)
nx.draw_networkx(
g,
pos,
)
return fig, pos

A* pathfinding heuristic for static nodes

I'm trying to write my own A* pathfinding algorithm and have a problem; when seeking the shortest cost path the following error arises:
Where the green filled node is the start node, the red filled node is the goal node and the pink filled nodes are the actual path that the algorithm has taken.
This is of course not the shortest path and I'm certain the problem lies with my heuristic function (manhattan distance):
int Map::manhattanDistance(Node evaluatedNode, Node goalNode)
{
int x = evaluatedNode.xCoord - goalNode.xCoord;
int y = evaluatedNode.yCoord - goalNode.yCoord;
if(x + y < 0)
return (x + y) * -1;
else
return x + y;
}
It seems to me that manhattan distance formula is not ideal here and my question is therefore: what other distance measuring algorithm would work for me here?
EDIT: for sake of clarity, each nodes width is determined by number of nodes in row / screen width = 640 and each nodes height is determined by number of nodes in column / screen height = 480.
A negative cost would imply that a route is free, or more than that it discounts previous moves. It looks like you have tried to account for this by multiplying by -1 of the aggregate, however the damage has already been done. That is why the solution has used diagonals, (1 - 1) * -1 is still zero and thus is considered to be free.
I would replace the if statement with the following, which uses the magnitude of each component by applying abs against it separately:
return Math.abs(x) + Math.abs(y);

Find all tiles intersected by line segment

I have to find all tiles that intersected by line segment but Bresenham's line algorithm doesnt fit to my requirements. I need to find all cells. I dont need to know intersection points, only the fact of intersection. Thanks for help.
I thought to find direction vector of line and step by step find cells by division on tile size. But i dont know how to select correct step size. 1 px step is bad i think.
Here is article of Amanatides and Woo "A Fast Voxel Traversal Algorithm for Ray Tracing" for 2D and 3D cases. Practical implementation.
You might use one of the many line equations found at: http://www.cut-the-knot.org/Curriculum/Calculus/StraightLine.shtml or http://mathworld.wolfram.com/Line.html
Supposedly you have your line in your coordinate system going through two points you deduce the y=mx+n equation and just match against your tiles and see if they intersect while moving x in the unit of your coordinate system in any direction you prefer from the smallest x of your tiles till the biggest. If your coordinate system is the screen, 1 pixel should be enough.
This is the closes I can hint right know without knowing more about the exact nature of the problem you are facing.
It is easy to modify the Bresenham's algorithm such that it tracks what you need. Here's the relevant fragment of the algorithm:
plot(x,y);
error = error + deltaerr;
if (error >= 0.5)
{
y = y + ystep;
error = error - 1.0;
}
To keep track of all the cells we need another variable. Note tat I have not rigorously checked this.
plot(x,y);
olderror = error.
error = error + deltaerr;
if (error >= 0.5)
{
y = y + ystep;
error = error - 1.0;
extra = error+olderror;
if (extra > 0)
{
plot (x,y); /* not plot (x-1,y); */
}
else if (extra < 0)
{
plot (x+1,y-1); /* not plot (x+1,y); */
}
else
{
// the line goes right through the cell corner
// either do nothing, or do both plot (x-1,y) and plot (x+1,y)
// depending on your definition of intersection
}
}