finding shortest path given distance transform image - python-2.7

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...

Related

Schrodinger equation not evolving properly with time?

I'm writing a code in python to evolve the time-dependent Schrodinger equation using the Crank-Nicolson scheme. I didn't know how to deal with the potential so I looked around and found a way from this question, which I have verified from a couple other sources. According to them, for a harmonic oscillator potential, the C-N scheme gives
AΨn+1=A∗Ψn
where the elements on the main diagonal of A are dj=1+[(iΔt) / (2m(Δx)^2)]+[(iΔt(xj)^2)/4] and the elements on the upper and lower diagonals are a=−iΔt/[4m(Δx)^2]
The way I understand it, I'm supposed to give an initial condition(I've chosen a coherent state) in the form of the matrix Ψn and I need to compute the matrix Ψn+1 , which is the wave function after time Δt. To obtain Ψn+1 for a given step, I'm inverting the matrix A and multiplying it with the matrix A* and then multiplying the result with Ψn. The resulting matrix then becomes Ψn for the next step.
But when I'm doing this, I'm getting an incorrect animation. The wave packet is supposed to oscillate between the boundaries but in my animation, it is barely moving from its initial mean value. I just don't understand what I'm doing wrong. Is my understanding of the problem wrong? Or is it a flaw in my code?Please help! I've posted my code below and the video of my animation here. I'm sorry for the length of the code and the question but it's driving me crazy not knowing what my mistake is.
import numpy as np
import matplotlib.pyplot as plt
L = 30.0
x0 = -5.0
sig = 0.5
dx = 0.5
dt = 0.02
k = 1.0
w=2
K=w**2
a=np.power(K,0.25)
xs = np.arange(-L,L,dx)
nn = len(xs)
mu = k*dt/(dx)**2
dd = 1.0+mu
ee = 1.0-mu
ti = 0.0
tf = 100.0
t = ti
V=np.zeros(len(xs))
u=np.zeros(nn,dtype="complex")
V=K*(xs)**2/2 #harmonic oscillator potential
u=(np.sqrt(a)/1.33)*np.exp(-(a*(xs - x0))**2)+0j #initial condition for wave function
u[0]=0.0 #boundary condition
u[-1] = 0.0 #boundary condition
A = np.zeros((nn-2,nn-2),dtype="complex") #define A
for i in range(nn-3):
A[i,i] = 1+1j*(mu/2+w*dt*xs[i]**2/4)
A[i,i+1] = -1j*mu/4.
A[i+1,i] = -1j*mu/4.
A[nn-3,nn-3] = 1+1j*mu/2+1j*dt*xs[nn-3]**2/4
B = np.zeros((nn-2,nn-2),dtype="complex") #define A*
for i in range(nn-3):
B[i,i] = 1-1j*mu/2-1j*w*dt*xs[i]**2/4
B[i,i+1] = 1j*mu/4.
B[i+1,i] = 1j*mu/4.
B[nn-3,nn-3] = 1-1j*(mu/2)-1j*dt*xs[nn-3]**2/4
X = np.linalg.inv(A) #take inverse of A
plt.ion()
l, = plt.plot(xs,np.abs(u),lw=2,color='blue') #plot initial wave function
T=np.matmul(X,B) #multiply A inverse with A*
while t<tf:
u[1:-1]=np.matmul(T,u[1:-1]) #updating u but leaving the boundary conditions unchanged
l.set_ydata((abs(u))) #update plot with new u
t += dt
plt.pause(0.00001)
After a lot of tinkering, it came down to reducing my step size. That did the job for me- I reduced the step size and the program worked. If anyone is facing the same problem as I am, I recommend playing around with the step sizes. Provided that the rest of the code is fine, this is the only possible area of error.

Trying to fit a sine function to phased light curve

import numpy as np
import matplotlib.pyplot as plt
from lmfit import Model,Parameters
f2= "KELT_N16_lc_006261_V01_west_tfa.dat"
t2="TIMES" # file name
NewData2 = np.loadtxt(t2, dtype=float, unpack=True)
NewData = np.loadtxt(f2,dtype=float, unpack=True, usecols=(1,))
flux = NewData
time= NewData2
new_flux=np.hstack([flux,flux])
# fold
period = 2.0232 # period (must be known already!)
foldTimes = ((time)/ period) # divide by period to convert to phase
foldTimes = foldTimes % 1 # take fractional part of phase only (i.e. discard whole number part)
new_phase=np.hstack([foldTimes+1,foldTimes])
print len(new_flux)
print len(new_phase)
def Wave(x, new_flux,new_phase):
wave = new_flux*np.sin(new_phase+x)
return wave
model = Model(Wave)
print "Independent Vars:", model.independent_vars
print "Parameters:",model.param_names
p = Parameters()
p.add_many(('new_flux',13.42, True, None, None, None) )
p.add_many(('new_phase',0,True, None, None, None) )
result=model.fit(new_flux,x=new_phase,params=p,weights= None)
plt.scatter(new_phase,new_flux,marker='o',edgecolors='none',color='blue',s=5.0, label="Period: 2.0232 days")
plt.ylim([13.42,13.54])
plt.xlim(0,2)
plt.gca().invert_yaxis()
plt.title('HD 240121 Light Curve with BJD Correction')
plt.ylabel('KELT Instrumental Magnitude')
plt.xlabel('Phase')
legend = plt.legend(loc='lower right', shadow=True)
plt.scatter(new_phase,result.best_fit,label="One Oscillation Fit", color='red',s=60.0)
plt.savefig('NewEpoch.png')
print result.fit_report()
I am trying to fit a sine function to phased light curve data for a research project. However, I am unsure as to where I am going wrong, and I believe it lays in my parameters. It appears that the fit has an amplitude that is too high, and a period that is too long. Any help would be appreciated. Thank you!
This is what the graph looks like now (Attempt at fitting a sine function to my dataset):
A couple of comments/suggestions:
First, it is almost certainly better to replace
p = Parameters()
p.add_many(('new_flux',13.42, True, None, None, None) )
p.add_many(('new_phase',0,True, None, None, None) )
with
p = Parameters()
p.add('new_flux', value=13.42, vary=True)
p.add('new_phase', value=0, vary=True)
Second, your model does not include a DC offset, but your data clearly has one. The offset is approximately 13.4 and the amplitude of the sine wave is approximately 0.05. While you're at it, you probably want to include a scale the phase as a well as an offset, so that the model is
offset + amplitude * sin(scale*x + phase_shift)
You don't necessarily have to vary all of those, but making your model more general will allow to see how the phase shift and scale are correlated -- given the noise level in your data, that might be important.
With the more general model, you can try a few sets of parameter values, using model.eval() to evaluate a model with a set of Parameters. Once you have a better model and reasonable starting points, you should get a reasonable fit.
How could we help you with your uncommented code?
How do we know what is what and what should it do?
What method for fitting are you using?
Where is the data and in what form ?
I would start with computing the approximate sin wave parameters. Let assume you got some input data in form of n points with x,y coordinates. And want to fit a sin wave:
y(t) = y0+A*sin(x0+x(t)*f)
Where y0 is the y offset, x0 is phase offset, A is amplitude and f is angular frequency.
I would:
Compute avg y value
y0 = sum(data[i].y)/n where i={0,1,2,...n-1}
this is the mean value representing possible y offset y0 of your sin wave.
compute avg distance to y0
d = sum (|data[i].y-y0|)/n where i={0,1,2,...n-1}
If my memory serves well this should be the effective value of amplitude so:
A = sqrt(2)*d
find zero crossings in the dataset
for this the dataset should be sorted by x so sort it if it is not. Remember index i for: first crossing i0, last crossing i1 and number of crossings found j from this we can estimate frequency and phase offset:
f=M_PI*double(j-1)/(datax[i1]-datax[i0]);
x0=-datax[i0]*f;
To determine which half sin wave we aligned to just check the sign of middle point between first two zero crossings
i1=i0+((i1-i0)/(j-1));
if (datay[(i0+i1)>>1]<=y0) x0+=M_PI;
Or check for specific zero crossing pattern instead.
That is all now we have approximate x0,y0,f,A parametters of sinwave.
Here some C++ code I tested with (sorry I do not use Python):
//---------------------------------------------------------------------------
#include <math.h>
// input data
const int n=5000;
double datax[n];
double datay[n];
// fitted sin wave
double A,x0,y0,f;
//---------------------------------------------------------------------------
void data_generate() // genere random noisy sinvawe
{
int i;
double A=150.0,x0=250.0,y0=200.0,f=0.03,r=20.0;
double x,y;
Randomize();
for (i=0;i<n;i++)
{
x=800.0*double(i)/double(n);
y=y0+A*sin(x0+x*f);
datax[i]=x+r*Random();
datay[i]=y+r*Random();
}
}
//---------------------------------------------------------------------------
void data_fit() // find raw approximate of x0,y0,f,A
{
int i,j,e,i0,i1;
double x,y,q0,q1;
// y0 = avg(y)
for (y0=0.0,i=0;i<n;i++) y0+=datay[i]; y0/=double(n);
// A = avg(|y-y0|)
for (A=0.0,i=0;i<n;i++) A+=fabs(datay[i]-y0); A/=double(n); A*=sqrt(2.0);
// bubble sort data by x asc
for (e=1,j=n;e;j--)
for (e=0,i=1;i<j;i++)
if (datax[i-1]>datax[i])
{
x=datax[i-1]; datax[i-1]=datax[i]; datax[i]=x;
y=datay[i-1]; datay[i-1]=datay[i]; datay[i]=y;
e=1;
}
// find zero crossings
for (i=0,j=0;i<n;)
{
// find value below zero
for (;i<n;i++) if (datay[i]-y0<=-0.75*A) break; e=i;
// find value above zero
for (;i<n;i++) if (datay[i]-y0>=+0.75*A) break;
if (i>=n) break;
// find point closest to zero
for (i1=e;e<i;e++)
if (fabs(datay[i1]-y0)>fabs(datay[e]-y0)) i1=e;
if (!j) i0=i1; j++;
}
f=2.0*M_PI*double(j-1)/(datax[i1]-datax[i0]);
x0=-datax[i0]*f;
}
//---------------------------------------------------------------------------
And preview:
The dots are generated noisy data and blue curve is fitted sin wave.
On top of all this you can build your fitting to increase precision. Does not matter which method you will use for the search around found parameters. For example I would go for:
How approximation search works

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

Counting the point which intercept in a line with opencv python

I am working in vehicle counting with opencv and python programming, I already complete step:
1. Detect moving vehicle with BackgroundSubtractorMOG2
2. Draw rectangle on it, then poin a centroid of that
3. Draw a line (to indicate of the counting)
if that centroid accros/intercept with the line I want count that 1. but in my code sometime it add sometime no. Here the line code:
cv2.line(frame,(0,170),(300,170),(200,200,0),2)
and there the centroid:
if w > 20 and h > 25:
cv2.rectangle(frame, (x,y), (x+w,y+h), (180, 1, 0), 1)
x1=w/2
y1=h/2
cx=x+x1
cy=y+y1
centroid=(cx,cy)
cv2.circle(frame,(int(cx),int(cy)),4,(0,255,0),-1)
my counting code:
if cy==170:
counter=counter+1
Can anyone help me. please. for your advice thankyou!
Here is my approach that would work independently of the video frame rate. Assuming that you are able to track a car's centroid at each frame, I would save the last two centroids' position (last_centroid and centroid in my code) and process as follows:
compute the intercepting line equation's parameters ( (a,b,c) from aX + bY + c = 0)
compute the equation's parameters of the segment line between last_centroid and centroid
find if the two lines are intersecting
if so, increment your counter
Here is how I implemented it in OpenCV (Python):
import cv2
import numpy as np
import collections
Params = collections.namedtuple('Params', ['a','b','c']) #to store equation of a line
def calcParams(point1, point2): #line's equation Params computation
if point2[1] - point1[1] == 0:
a = 0
b = -1.0
elif point2[0] - point1[0] == 0:
a = -1.0
b = 0
else:
a = (point2[1] - point1[1]) / (point2[0] - point1[0])
b = -1.0
c = (-a * point1[0]) - b * point1[1]
return Params(a,b,c)
def areLinesIntersecting(params1, params2, point1, point2):
det = params1.a * params2.b - params2.a * params1.b
if det == 0:
return False #lines are parallel
else:
x = (params2.b * -params1.c - params1.b * -params2.c)/det
y = (params1.a * -params2.c - params2.a * -params1.c)/det
if x <= max(point1[0],point2[0]) and x >= min(point1[0],point2[0]) and y <= max(point1[1],point2[1]) and y >= min(point1[1],point2[1]):
print("intersecting in:", x,y)
cv2.circle(frame,(int(x),int(y)),4,(0,0,255), -1) #intersecting point
return True #lines are intersecting inside the line segment
else:
return False #lines are intersecting but outside of the line segment
cv2.namedWindow('frame')
frame = np.zeros((240,320,3), np.uint8)
last_centroid = (200,200) #centroid of a car at t-1
centroid = (210,180) #centroid of a car at t
line_params = calcParams(last_centroid, centroid)
intercept_line_params = calcParams((0,170), (300,170))
print("Params:", line_params.a,line_params.b,line_params.c)
while(1):
cv2.circle(frame,last_centroid,4,(0,255,0), -1) #last_centroid
cv2.circle(frame,centroid,4,(0,255,0), -1) #current centroid
cv2.line(frame,last_centroid,centroid,(0,0,255),1) #segment line between car centroid at t-1 and t
cv2.line(frame,(0,170),(300,170),(200,200,0),2) #intercepting line
print("AreLinesIntersecting: ",areLinesIntersecting(intercept_line_params,line_params,last_centroid,centroid))
cv2.imshow('frame',frame)
if cv2.waitKey(15) & 0xFF == ord('q'):
break
cv2.destroyAllWindows()
And here are some results:
Fig1. Segment is intersecting the line (intercepting line in blue - segment line between last_centroid and centroid in red)
Fig2. Segment is NOT intersecting the line
N.B. I found the formulas to calculate the intersection point here.
I hope my approach will help to address your problem.
To assume that the centroid will assume a position 170 (in x or y) is wrong, because videos generally works at 30 fps, that mean you will get 30 centroid locations per second which means even if there the object crosses the line, it may never be 170!
To counter this, one method that can be used is defining a line margin. This means now you have a line margin x before actual line (y = 170) and x after the line margin.
So if your object falls anywhere in the margin, you can increment the counter. Now the next big part would be to make a tracking mechanism wherein you collect the list of point for each object and check if it fell in the margin.

Building tree/graph from image points

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