I have a grid of (d) dimensions, all dimensions are partitioned using delta = 0.25
An example of this grid is this figure (d here is 2, and each dimension is normalized 0-1):
Each intersection represents 1 point, for example, the point in the middle is represented as:
double[] A={0.5, 0.5};
My question is:
I want to search this grid, starting from an input point A and its neighbors. Then continue doing that. With one condition: Each point is visited only once.
To clarify more, consider this example:
The starting point is:
double[] A={0.5, 0.5};
So, A is checked first, then its neighbours are generated and inserted into a Queue (the queue is ordered based on a function f).
Here point A is the dark circle . Its neighbors are the green circles:
{0.25, 0.25}
{0.25, 0.5}
{0.25, 0.75}
..
..
..
{0.75, 0.75}
Now, the algorithm loops until the Queue becomes empty.
In the loop, the top point is removed (pop()), then checked, then its neighbors are added to the Queue.
For example, in the first loop, the blue circle happened to be the top point in the Queue.
It is removed from the Queue, then checked, then its neighbors (red circles) are generated and added to the Queue.
The problem here is, my code that generates the neighbors points, does not know if a previous point is visited before or not.
and I cannot keep a list of previously visited points, and check it every time a new point is generated ( with high dimensions and high resolution e.g., d=8 and delta= 0.03125, it will take for ever!)
This is the search algorithm:
public void search(int d, double delta, double[] inpoint)
{
Queue queue = new Queue();
queue.push(inpoint);
while( !queue.isEmpty() )
{
// remove top point from Queue
double[] a = queue.pop();
// Check point a and do some calculations
// ;;;;;;;;;;;;;;;;
// Generate neighbours of current point:
ArrayList<double[]> neighbors = new ArrayList<double[]>();
nextMoves(a, new double[d], delta, d, neighbors);
// For each point in neighbors, push to Queue:
for( int i=0 ; i < neighbors.size(), i++ )
queue.push(neighbors.get(i));
}
}
And this is the algorithm for generating the neighbors, it is a recursive algorithm.
public static void nextMoves(double[] inatt, double[] outatt, double delta, int d, ArrayList<double[]> neighbors)
{
if( d == inatt.length )
{
if( !outOfBound(outatt,d) )
{
moves.add(outatt);
}
}
else
{
// first case: add delta:
outatt[d] = inatt[d]+delta;
nextMoves( inatt, outatt, delta, d+1, moves);
// second case: subtract delta:
outatt[d] = inatt[d]-delta;
nextMoves( inatt, outatt, delta, d+1, moves);
// third case: no change:
outatt[d] = inatt[d];
nextMoves( inatt, outatt, delta, d+1, moves);
}
}
As I mentioned before, keeping a list of previously visited points is not a possible solution.
If I do this, then the list becomes very large when I have high dimensionality and high resolution.
And this list will have to be searched linearly each time a point is created!
Perhaps I should organize this list in a spatial index? I don't know...
I would appreciate your input.
There are a couple of potential shortcuts you could consider:
You could remove points from your 'previously visited' list once all surrounding points have been added. The reasoning is simple: they can only be added from surrounding points. This means only the surface of the visited volume needs to be in the list. At higher dimensions this would be a substantial saving.
More complicated would be to store the shape of the volume rather than the points. This would mean recording each inflection point on the surface of the volume and testing if each new point is on that surface.
The first is simpler but not as efficient. I'd suggest starting with that and seeing if it's enough.
You dont have to store all visited points. Going around and collect first all neighbours and build a hashkey out of this points to store this in a hashmap. Then validate all this points with your code and collect the next circle of neighbours. In the worst case you started in the middle. Then the calculationtime of the hashkeys are high at the end of the algortihm. To solve this problem you can build cluster and search within the cluster first.
You can start with clustering too. Build up a cluster layout and start to search within this cluster with the above circle method.
This sounds similar to a pathfinding problem where you might also parse a grid and don't unnecessarily want to revisit nodes.
If it works for you, set a boolean on each visited node, so if the algorithm happens upon it again, it knows it doesn't need to check it.
Look at Djikstras Algorithm for inspiration.
Related
I am writing a program that outputs the shortest route between two points on a map. The problem is that if the route is too long and it has many points that define its path it slows the program a lot and I am looking for a way to draw just some points instead of all the points in the array.
My approach goes as follows: the map has a zoom, each time the zoom changes check which points overlaps with the others. All the points that doesn't overlap go into the routeToDraw list and then it is drawn.
To check if the points overlap or not I have the following function:
//route is a list of latitude and longitude points
LinkedList<Point.Double> route = MapPanel.this.getGlassPane().getRoute();
LinkedList<Point.Double> routeToDraw = new LinkedList<Point.Double>();
int ovalSize = 8;
boolean compareMorePoints;
for(int i = 0; i < route.size(); i++) {
Point p1 = getScreenCoordinates(route.get(i).x, route.get(i).y);
compareMorePoints = true;
int j = i + 1;
while (j < route.size() && compareMorePoints == true) {
Point p2 = getScreenCoordinates(route.get(j).x, route.get(j).y);
if (Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)) > ovalSize ) {
routeToDraw.add(route.get(i));
compareMorePoints = false;
}
j++;
}
}
MapPanel.this.getGlassPane().setRouteToDraw(routeToDraw);
The problem is that this function is quite expensive and although it does reduce the amount of points to draw and I seem to obtain some speed after calculating routeToDraw I don't think it is worth the wait each time I zoom in or out.
The ideal solution would be something like Google Maps' does when routing, drawing a series of equidistant points that modify each time you zoom in or out and look quite nice.
Two suggestions...
(old trick)... Don't do unnecessary math inside of a loop. You can and should eliminate the sqrt function, which is an "expensive" math operation when doing distances. Just compare to the square of ovalSize. It is mathematically equivalent.
Is your list sorted in any way? If there were a convenient point in your program to sort your list (or a copy of it) before displaying, then you could very quickly:
Lop off the first and last part that is outside your zoom window in one of the coordinates (say X, if you sorted by X) by doing a binary search for the window boundary
Tighten up your loop to only look at neighbors within a window of concern, and do a sliding window instead of all-compared-to-all.
I'm using OpenCV to get an birdseye view of the captured frames. This is done by providing a chessboard pattern on the plane which will form the birdseye view.
Although it seems like the camera is already pretty on top of this plain, I need it to be perfect in order to determine the relationship between pixels and centimeters.
In the next phase the captures frames are being warped. It gives the expected result:
However, by performing this transformation, data outside the chessboard pattern is being lost. What I need is rotating the image instead of warping a known quadrangle.
Question: How to rotate an image by an camera angle so that it's top-down?
Some code to illustrate what I'm currently doing:
Size chessboardSize = new Size(12, 8); // Size of the chessboard
Size captureSize = new Size(1920, 1080); // Size of the captured frames
Size viewSize = new Size((chessboardSize.width / chessboardSize.height) * captureSize.height, captureSize.height); // Size of the view
MatOfPoint2f imageCorners; // Contains the imageCorners obtained in a earlier stage
Mat H; // Homography
The code which finds the corners:
Mat grayImage = new Mat();
//Imgproc.resize(source, temp, new Size(source.width(), source.height()));
Imgproc.cvtColor(source, grayImage, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(grayImage, grayImage, 0.0, 255.0, Imgproc.THRESH_OTSU);
imageCorners = new MatOfPoint2f();
Imgproc.GaussianBlur(grayImage, grayImage, new Size(5, 5), 5);
boolean found = Calib3d.findChessboardCorners(grayImage, chessboardSize, imageCorners, Calib3d.CALIB_CB_NORMALIZE_IMAGE + Calib3d.CALIB_CB_ADAPTIVE_THRESH + Calib3d.CALIB_CB_FILTER_QUADS);
if (found) {
determineHomography();
}
The code which determines the homography:
Point[] data = imageCorners.toArray();
if (data.length < chessboardSize.area()) {
return;
}
Point[] roi = new Point[] {
data[0 * (int)chessboardSize.width - 0], // Top left
data[1 * (int)chessboardSize.width - 1], // Top right
data[((int)chessboardSize.height - 1) * (int)chessboardSize.width - 0], // Bottom left
data[((int)chessboardSize.height - 0) * (int)chessboardSize.width - 1], // Bottom right
};
Point[] roo = new Point[] {
new Point(0, 0),
new Point(viewSize.width, 0),
new Point(0, viewSize.height),
new Point(viewSize.width, viewSize.height)
};
MatOfPoint2f objectPoints = new MatOfPoint2f(), imagePoints = new MatOfPoint2f();
objectPoints.fromArray(roo);
imagePoints.fromArray(roi);
Mat H = Imgproc.getPerspectiveTransform(imagePoints, objectPoints);
Finally, the captured frames are being warped:
Imgproc.warpPerspective(capture, view, H, viewSize);
[Edit2] updated progress
There may be more then just rotation present so I would try this instead:
preprocess image
You can apply many filters to remove noise from the image and or normalize illumination conditions (looks like your posted image does not need it). Then simply binarise the image to simplify further steps. see related:
OpenCV for OCR: How to compute thresholding levels for gray image OCR
detect square corner points
and store their coordinates in some array with their topology
double pnt[col][row][2];
where (col,row) is the chessboard index and [2] stores (x,y). You can use int but double/float will avoid unnecessary conversions and rounding during fitting ...
The corners can be detected (unless skew/rotation is near 45 degrees) by scanning the diagonal neighbor pixels like this:
One diagonal should be in one color and the other one in different. This pattern will detect cluster of points around the crossing so find close such points and compute their average.
If you scan the whole image the upper for cycle axis will also sort the point list so no need for further sorting. After averaging sort/order the points to the grid topology (for example by direction between 2 closest points)
Topology
To make it robust I use rotated&skewed image so the topology detection is a bit tricky. After a while of elaborating I come to this:
find point p0 near middle of image
That should ensure that there are neighbors for that point.
find closest point p to it
But ignore diagonal points (|x/y| -> 1 +/- scale of squares). From this point compute first basis vector, let call it u for now.
find closest point p to it
In te same manner as #2 but this time also ignore points in +/-u direction (|(u.v)|/(|u|.|v|) -> 1 +/- skew/rotations). From this point compute second basis vector, let call it v for now.
normalize u,v
I chose that u vector points to +x and v to +ydirection. So basis vector with bigger |x| value should be u and with bigger |y| should be v. So test and swap if needed. Then just negate if the wrong sign. Now we have basis vectors for middle of screen (further away they might change).
compute topology
Set the p0 point as (u=0,v=0) as a start point. Now loop through all yet unmatched points p. For each compute predicted position of the neighbors by adding/substracting basis vectors from its position. Then find closest point to this location and if found it should be neighbor so set its (u,v) coordinate to +/-1 of the original point p. Now update the basis vectors for these points and loop the whole thing until no new match found. The result should be that most of the points should have computed their (u,v) coordinates which is what we need.
After this you can find the min(u),min(v) and shift it to (0,0) so the indexes are not negative if needed.
fit a polynomial for the corner points
for example something like:
pnt[i][j][0]=fx(i,j)
pnt[i][j][1]=fy(i,j)
where fx,fy are polynomial functions. You can try any fitting process. I tried cubic polynomial fit with usage of approximation search but the result was not as good as native bi-cubic interpolation(possibly because of non uniform distortion of test image) so I switched to bi-cubic interpolation instead of fitting. That is more simple but makes computing inverse very difficult, but it can be avoided at cost of speed. If you need to compute the inverse anyway see
Reverse complex 2D lookup table
I am using simple interpolation cubic like this:
d1=0.5*(pp[2]-pp[0]);
d2=0.5*(pp[3]-pp[1]);
a0=pp[1];
a1=d1;
a2=(3.0*(pp[2]-pp[1]))-(2.0*d1)-d2;
a3=d1+d2+(2.0*(-pp[2]+pp[1])); }
coordinate = a0+(a1*t)+(a2*t*t)+(a3*t*t*t);
where pp[0..3] are 4 consequent known control points (our grid crossings), a0..a3 are computed polynomial coefficients and coordinate is point on curve with parameter t. This can be expanded to any number of dimensions.
The properties of this curve are simple it is continuous, starting at pp[1] and ending at pp[2] while t=<0.0,1.0>. The continuity with neighboring segments is ensured with sequence common to all cubic curves.
remap pixels
simply use i,j as floating values with step around 75% of pixel size to avoid gaps. Then just simply loop through all the positions (i,j) compute (x,y) and copy pixel from source image at (x,y) to (i*sz,j*sz)+/-offset where sz is wanted grid size in pixels.
Here the C++:
//---------------------------------------------------------------------------
picture pic0,pic1; // pic0 - original input image,pic1 output
//---------------------------------------------------------------------------
struct _pnt
{
int x,y,n;
int ux,uy,vx,vy;
_pnt(){};
_pnt(_pnt& a){ *this=a; };
~_pnt(){};
_pnt* operator = (const _pnt *a) { x=a->x; y=a->y; return this; };
//_pnt* operator = (const _pnt &a) { ...copy... return this; };
};
//---------------------------------------------------------------------------
void vision()
{
pic1=pic0; // copy input image pic0 to pic1
pic1.enhance_range(); // maximize dynamic range of all channels
pic1.treshold_AND(0,127,255,0); // binarize (remove gray shades)
pic1&=0x00FFFFFF; // clear alpha channel for exact color matching
pic1.save("out_binarised.png");
int i0,i,j,k,l,x,y,u,v,ux,uy,ul,vx,vy,vl;
int qi[4],ql[4],e,us,vs,**uv;
_pnt *p,*q,p0;
List<_pnt> pnt;
// detect square crossings point clouds into pnt[]
pnt.allocate(512); pnt.num=0;
p0.ux=0; p0.uy=0; p0.vx=0; p0.vy=0;
for (p0.n=1,p0.y=2;p0.y<pic1.ys-2;p0.y++) // sorted by y axis, each point has usage n=1
for ( p0.x=2;p0.x<pic1.xs-2;p0.x++)
if (pic1.p[p0.y-2][p0.x+2].dd==pic1.p[p0.y+2][p0.x-2].dd)
if (pic1.p[p0.y-1][p0.x+1].dd==pic1.p[p0.y+1][p0.x-1].dd)
if (pic1.p[p0.y-1][p0.x+1].dd!=pic1.p[p0.y+1][p0.x+1].dd)
if (pic1.p[p0.y-1][p0.x-1].dd==pic1.p[p0.y+1][p0.x+1].dd)
if (pic1.p[p0.y-2][p0.x-2].dd==pic1.p[p0.y+2][p0.x+2].dd)
pnt.add(p0);
// merge close points (deleted point has n=0)
for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
if (p->n) // skip deleted points
for (p0=*p,j=i+1,q=p+1;j<pnt.num;j++,q++) // scan all remaining points
if (q->n) // skip deleted points
{
if (q->y>p0.y+4) continue; // scan only up do y distance <=4 (clods are not bigger then that)
x=p0.x-q->x; x*=x; // compute distance^2
y=p0.y-q->y; y*=y; x+=y;
if (x>25) continue; // skip too distant points
p->x+=q->x; // add coordinates (average)
p->y+=q->y;
p->n++; // increase ussage
q->n=0; // mark current point as deleted
}
// divide the average coordinates and delete marked points
for (p=pnt.dat,i=0,j=0;i<pnt.num;i++,p++)
if (p->n) // skip deleted points
{
p->x/=p->n;
p->y/=p->n;
p->n=1;
pnt.dat[j]=*p; j++;
} pnt.num=j;
// n is now encoded (u,v) so set it as unmatched (u,v) first
#define uv2n(u,v) ((((v+32768)&65535)<<16)|((u+32768)&65535))
#define n2uv(n) { u=n&65535; u-=32768; v=(n>>16)&65535; v-=32768; }
for (p=pnt.dat,i=0;i<pnt.num;i++,p++) p->n=0;
// p0,i0 find point near middle of image
x=pic1.xs>>2;
y=pic1.ys>>2;
for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
if ((p->x>=x)&&(p->x<=x+x+x)
&&(p->y>=y)&&(p->y<=y+y+y)) break;
p0=*p; i0=i;
// q,j find closest point to p0
vl=pic1.xs+pic1.ys; k=0;
for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
if (i!=i0)
{
x=p->x-p0.x;
y=p->y-p0.y;
l=sqrt((x*x)+(y*y));
if (abs(abs(x)-abs(y))*5<l) continue; // ignore diagonals
if (l<=vl) { k=i; vl=l; } // remember smallest distance
}
q=pnt.dat+k; j=k;
ux=q->x-p0.x;
uy=q->y-p0.y;
ul=sqrt((ux*ux)+(uy*uy));
// q,k find closest point to p0 not in u direction
vl=pic1.xs+pic1.ys; k=0;
for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
if (i!=i0)
{
x=p->x-p0.x;
y=p->y-p0.y;
l=sqrt((x*x)+(y*y));
if (abs(abs(x)-abs(y))*5<l) continue; // ignore diagonals
if (abs((100*ux*y)/((x*uy)+1))>75) continue;// ignore paralel to u directions
if (l<=vl) { k=i; vl=l; } // remember smallest distance
}
q=pnt.dat+k;
vx=q->x-p0.x;
vy=q->y-p0.y;
vl=sqrt((vx*vx)+(vy*vy));
// normalize directions u -> +x, v -> +y
if (abs(ux)<abs(vx))
{
x=j ; j =k ; k =x;
x=ux; ux=vx; vx=x;
x=uy; uy=vy; vy=x;
x=ul; ul=vl; vl=x;
}
if (abs(vy)<abs(uy))
{
x=ux; ux=vx; vx=x;
x=uy; uy=vy; vy=x;
x=ul; ul=vl; vl=x;
}
x=1; y=1;
if (ux<0) { ux=-ux; uy=-uy; x=-x; }
if (vy<0) { vx=-vx; vy=-vy; y=-y; }
// set (u,v) encoded in n for already found points
p0.n=uv2n(0,0); // middle point
p0.ux=ux; p0.uy=uy;
p0.vx=vx; p0.vy=vy;
pnt.dat[i0]=p0;
p=pnt.dat+j; // p0 +/- u basis vector
p->n=uv2n(x,0);
p->ux=ux; p->uy=uy;
p->vx=vx; p->vy=vy;
p=pnt.dat+k; // p0 +/- v basis vector
p->n=uv2n(0,y);
p->ux=ux; p->uy=uy;
p->vx=vx; p->vy=vy;
// qi[k],ql[k] find closest point to p0
#define find_neighbor \
for (ql[k]=0x7FFFFFFF,qi[k]=-1,q=pnt.dat,j=0;j<pnt.num;j++,q++) \
{ \
x=q->x-p0.x; \
y=q->y-p0.y; \
l=(x*x)+(y*y); \
if (ql[k]>=l) { ql[k]=l; qi[k]=j; } \
}
// process all matched points
for (e=1;e;)
for (e=0,p=pnt.dat,i=0;i<pnt.num;i++,p++)
if (p->n)
{
// prepare variables
ul=(p->ux*p->ux)+(p->uy*p->uy);
vl=(p->vx*p->vx)+(p->vy*p->vy);
// find neighbors near predicted position p0
k=0; p0.x=p->x-p->ux; p0.y=p->y-p->uy; find_neighbor; if (ql[k]<<1>ul) qi[k]=-1; // u-1,v
k++; p0.x=p->x+p->ux; p0.y=p->y+p->uy; find_neighbor; if (ql[k]<<1>ul) qi[k]=-1; // u+1,v
k++; p0.x=p->x-p->vx; p0.y=p->y-p->vy; find_neighbor; if (ql[k]<<1>vl) qi[k]=-1; // u,v-1
k++; p0.x=p->x+p->vx; p0.y=p->y+p->vy; find_neighbor; if (ql[k]<<1>vl) qi[k]=-1; // u,v+1
// update local u,v basis vectors for found points (and remember them)
n2uv(p->n); ux=p->ux; uy=p->uy; vx=p->vx; vy=p->vy;
k=0; if (qi[k]>=0) { q=pnt.dat+qi[k]; if (!q->n) { e=1; q->n=uv2n(u-1,v); q->ux=-(q->x-p->x); q->uy=-(q->y-p->y); } ux=q->ux; uy=q->uy; }
k++; if (qi[k]>=0) { q=pnt.dat+qi[k]; if (!q->n) { e=1; q->n=uv2n(u+1,v); q->ux=+(q->x-p->x); q->uy=+(q->y-p->y); } ux=q->ux; uy=q->uy; }
k++; if (qi[k]>=0) { q=pnt.dat+qi[k]; if (!q->n) { e=1; q->n=uv2n(u,v-1); q->vx=-(q->x-p->x); q->vy=-(q->y-p->y); } vx=q->vx; vy=q->vy; }
k++; if (qi[k]>=0) { q=pnt.dat+qi[k]; if (!q->n) { e=1; q->n=uv2n(u,v+1); q->vx=+(q->x-p->x); q->vy=+(q->y-p->y); } vx=q->vx; vy=q->vy; }
// copy remembered local u,v basis vectors to points where are those missing
k=0; if (qi[k]>=0) { q=pnt.dat+qi[k]; if (!q->vy) { q->vx=vx; q->vy=vy; }}
k++; if (qi[k]>=0) { q=pnt.dat+qi[k]; if (!q->vy) { q->vx=vx; q->vy=vy; }}
k++; if (qi[k]>=0) { q=pnt.dat+qi[k]; if (!q->ux) { q->ux=ux; q->uy=uy; }}
k++; if (qi[k]>=0) { q=pnt.dat+qi[k]; if (!q->ux) { q->ux=ux; q->uy=uy; }}
}
// find min,max (u,v)
ux=0; uy=0; vx=0; vy=0;
for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
if (p->n)
{
n2uv(p->n);
if (ux>u) ux=u;
if (vx>v) vx=v;
if (uy<u) uy=u;
if (vy<v) vy=v;
}
// normalize (u,v)+enlarge and create topology table
us=uy-ux+1;
vs=vy-vx+1;
uv=new int*[us];
for (u=0;u<us;u++) uv[u]=new int[vs];
for (u=0;u<us;u++)
for (v=0;v<vs;v++)
uv[u][v]=-1;
for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
if (p->n)
{
n2uv(p->n);
u-=ux; v-=vx;
p->n=uv2n(u,v);
uv[u][v]=i;
}
// bi-cubic interpolation
double a0,a1,a2,a3,d1,d2,pp[4],qx[4],qy[4],t,fu,fv,fx,fy;
// compute cubic curve coefficients a0..a3 from 1D points pp[0..3]
#define cubic_init { d1=0.5*(pp[2]-pp[0]); d2=0.5*(pp[3]-pp[1]); a0=pp[1]; a1=d1; a2=(3.0*(pp[2]-pp[1]))-(2.0*d1)-d2; a3=d1+d2+(2.0*(-pp[2]+pp[1])); }
// compute cubic curve cordinates =f(t)
#define cubic_xy (a0+(a1*t)+(a2*t*t)+(a3*t*t*t));
// safe access to grid (u,v) point copies it to p0
// points utside grid are computed by mirroring
#define point_uv(u,v) \
{ \
if ((u>=0)&&(u<us)&&(v>=0)&&(v<vs)) p0=pnt.dat[uv[u][v]]; \
else{ \
int uu=u,vv=v; \
if (uu<0) uu=0; \
if (uu>=us) uu=us-1; \
if (vv<0) vv=0; \
if (vv>=vs) vv=vs-1; \
p0=pnt.dat[uv[uu][vv]]; \
uu=u-uu; vv=v-vv; \
p0.x+=(uu*p0.ux)+(vv*p0.vx); \
p0.y+=(uu*p0.uy)+(vv*p0.vy); \
} \
}
//----------------------------------------
//--- Debug draws: -----------------------
//----------------------------------------
// debug recolor white to gray to emphasize debug render
pic1.recolor(0x00FFFFFF,0x00404040);
// debug draw basis vectors
for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
{
pic1.bmp->Canvas->Pen->Color=clRed;
pic1.bmp->Canvas->Pen->Width=1;
pic1.bmp->Canvas->MoveTo(p->x,p->y);
pic1.bmp->Canvas->LineTo(p->x+p->ux,p->y+p->uy);
pic1.bmp->Canvas->Pen->Color=clBlue;
pic1.bmp->Canvas->MoveTo(p->x,p->y);
pic1.bmp->Canvas->LineTo(p->x+p->vx,p->y+p->vy);
pic1.bmp->Canvas->Pen->Width=1;
}
// debug draw crossings
AnsiString s;
pic1.bmp->Canvas->Font->Height=12;
pic1.bmp->Canvas->Brush->Style=bsClear;
for (p=pnt.dat,i=0;i<pnt.num;i++,p++)
{
n2uv(p->n);
if (p->n)
{
pic1.bmp->Canvas->Font->Color=clWhite;
s=AnsiString().sprintf("%i,%i",u,v);
}
else{
pic1.bmp->Canvas->Font->Color=clGray;
s=i;
}
x=p->x-(pic1.bmp->Canvas->TextWidth(s)>>1);
y=p->y-(pic1.bmp->Canvas->TextHeight(s)>>1);
pic1.bmp->Canvas->TextOutA(x,y,s);
}
pic1.bmp->Canvas->Brush->Style=bsSolid;
pic1.save("out_topology.png");
// debug draw of bi-cubic interpolation fit/coveradge with half square step
pic1=pic0;
pic1.treshold_AND(0,200,0x40,0); // binarize (remove gray shades)
pic1.bmp->Canvas->Pen->Color=clAqua;
pic1.bmp->Canvas->Brush->Color=clBlue;
for (fu=-1;fu<double(us)+0.01;fu+=0.5)
for (fv=-1;fv<double(vs)+0.01;fv+=0.5)
{
u=floor(fu);
v=floor(fv);
// 4x cubic curve in v direction
t=fv-double(v);
for (i=0;i<4;i++)
{
point_uv(u-1+i,v-1); pp[0]=p0.x;
point_uv(u-1+i,v+0); pp[1]=p0.x;
point_uv(u-1+i,v+1); pp[2]=p0.x;
point_uv(u-1+i,v+2); pp[3]=p0.x;
cubic_init; qx[i]=cubic_xy;
point_uv(u-1+i,v-1); pp[0]=p0.y;
point_uv(u-1+i,v+0); pp[1]=p0.y;
point_uv(u-1+i,v+1); pp[2]=p0.y;
point_uv(u-1+i,v+2); pp[3]=p0.y;
cubic_init; qy[i]=cubic_xy;
}
// 1x cubic curve in u direction on the resulting 4 points
t=fu-double(u);
for (i=0;i<4;i++) pp[i]=qx[i]; cubic_init; fx=cubic_xy;
for (i=0;i<4;i++) pp[i]=qy[i]; cubic_init; fy=cubic_xy;
t=1.0;
pic1.bmp->Canvas->Ellipse(fx-t,fy-t,fx+t,fy+t);
}
pic1.save("out_fit.png");
// linearizing of original image
DWORD col;
double grid_size=32.0; // linear grid square size in pixels
double grid_step=0.01; // u,v step <= 1 pixel
pic1.resize((us+1)*grid_size,(vs+1)*grid_size); // resize target image
pic1.clear(0); // clear target image
for (fu=-1;fu<double(us)+0.01;fu+=grid_step) // copy/transform source image to target
for (fv=-1;fv<double(vs)+0.01;fv+=grid_step)
{
u=floor(fu);
v=floor(fv);
// 4x cubic curve in v direction
t=fv-double(v);
for (i=0;i<4;i++)
{
point_uv(u-1+i,v-1); pp[0]=p0.x;
point_uv(u-1+i,v+0); pp[1]=p0.x;
point_uv(u-1+i,v+1); pp[2]=p0.x;
point_uv(u-1+i,v+2); pp[3]=p0.x;
cubic_init; qx[i]=cubic_xy;
point_uv(u-1+i,v-1); pp[0]=p0.y;
point_uv(u-1+i,v+0); pp[1]=p0.y;
point_uv(u-1+i,v+1); pp[2]=p0.y;
point_uv(u-1+i,v+2); pp[3]=p0.y;
cubic_init; qy[i]=cubic_xy;
}
// 1x cubic curve in u direction on the resulting 4 points
t=fu-double(u);
for (i=0;i<4;i++) pp[i]=qx[i]; cubic_init; fx=cubic_xy; x=fx;
for (i=0;i<4;i++) pp[i]=qy[i]; cubic_init; fy=cubic_xy; y=fy;
// here (x,y) contains source image coordinates coresponding to grid (fu,fv) so copy it to col
col=0; if ((x>=0)&&(x<pic0.xs)&&(y>=0)&&(y<pic0.ys)) col=pic0.p[y][x].dd;
// compute liner image coordinates (x,y) by scaling (fu,fv)
fx=(fu+1.0)*grid_size; x=fx;
fy=(fv+1.0)*grid_size; y=fy;
// copy col to it
if ((x>=0)&&(x<pic1.xs)&&(y>=0)&&(y<pic1.ys)) pic1.p[y][x].dd=col;
}
pic1.save("out_linear.png");
// release memory and cleanup macros
for (u=0;u<us;u++) delete[] uv[u]; delete[] uv;
#undef uv2n
#undef n2uv
#undef find_neighbor
#undef cubic_init
#undef cubic_xy
#undef point_uv(u,v)
}
//---------------------------------------------------------------------------
Sorry I know its a lot of code but at least I commented it as much as I could. The code is not optimized for the sake of simplicity and understand-ability the final image linearization can be written a lot faster. Also I chose the grid_size and grid_step in that part of code manually. It should be computed from the image and known physical properties instead.
I use my own picture class for images so some members are:
xs,ys size of image in pixels
p[y][x].dd is pixel at (x,y) position as 32 bit integer type
clear(color) - clears entire image
resize(xs,ys) - resizes image to new resolution
bmp - VCL encapsulated GDI Bitmap with Canvas access
I also use mine dynamic list template so:
List<double> xxx; is the same as double xxx[];
xxx.add(5); adds 5 to end of the list
xxx[7] access array element (safe)
xxx.dat[7] access array element (unsafe but fast direct access)
xxx.num is the actual used size of the array
xxx.reset() clears the array and set xxx.num=0
xxx.allocate(100) preallocate space for 100 items
Here are the sub result output images. To make the stuff more robust I changed input image to more distorted one:
To make it visually more pleasing I recolored the white to gray. Red lines are local u basis and Blue are the local v basis vectors. White 2D vector numbers are topology (u,v) coordinates and gray scalar numbers are crossings index in pnt[] for topology yet unmatched points.
[Notes]
This approach will not work for rotations near 45 degree. For such cases you need to change the crossing detection from cross to plus pattern and also the topology conditions and equations changes a bit. Not to mention u,v direction selection.
I am trying to write something that will take a X and Y coordinate and a value that represents the available movement points the selected actor has. It should then output a list of the reachable locations so that I can highlight those tiles when the actor is choosing where to move.
I managed to use a function from my pathfinding library (https://github.com/xaguzman/pathfinding) that gives me the neighbouring tiles of a tile as a List of grid cells. It can also check my Tile-map and see if the tile is walkable.
What I can't really get my head around is how I would be able to set this up so that it would run as many times as the movement points.
List<GridCell> neighbours;
NavigationTiledMapLayer navLayer;
public void getMovementPossibilities(int tileX, int tileY) {
GridCell cell1;
GridCell cell2;
cell1 = navLayer.getCell(tileX, tileY);
GridFinderOptions opt = new GridFinderOptions();
opt.allowDiagonal = true;
opt.dontCrossCorners = false;
neighbours = navLayer.getNeighbors(cell1, opt);
for (int i = 0; i < neighbours.size(); i++) {
int nX = neighbours.get(i).getX();
int nY = neighbours.get(i).getY();
cell2 = navLayer.getCell(nX, nY);
neighbours.addAll(navLayer.getNeighbors(cell2, opt));
}
}
Sounds like a case for recursion. I can't see how you're keeping track of movement points but your current method finds all tiles 1 distance away. Inside this method, you need to recall the same method, but with each of those neighbours as your new start point and with the movement points reduced by 1.
This in turn generates all the second neighbours, and then the method will be called recursively and so on...
You will want a check at the top of the method so that if the distance points left are 0, it just immediately adds the current tile to neighbours and then returns (to stop the recursive chain going on forever).
I have a set of rectangles and I would like to "reduce" the set so I have the fewest number of rectangles to describe the same area as the original set. If possible, I would like it to also be fast, but I am more concerned with getting the number of rectangles as low as possible. I have an approach now which works most of the time.
Currently, I start at the top-left most rectangle and see if I can expand it out right and down while keeping it a rectangle. I do that until it can't expand anymore, remove and split all intersecting rectangles, and add the expanded rectangle back in the list. Then I start the process again with the next top-left most rectangle, and so on. But in some cases, it doesn't work. For example:
With this set of three rectangles, the correct solution would end up with two rectangles, like this:
However, in this case, my algorithm starts by processing the blue rectangle. This expand downwards and splits the yellow rectangle (correctly). But then when the remainder of the yellow rectangle is processed, instead of expanding downwards, it expands right first and takes back the portion that was previously split off. Then the last rectangle is processed and it can't expand right or down, so the original set of rectangles is left. I could tweak the algorithm to expand down first and then right. That would fix this case, but it would cause the same problem in a similar scenario that was flipped.
Edit: Just to clarify, the original set of rectangles do not overlap and do not have to be connected. And if a subset of rectangles are connected, the polygon which completely covers them can have holes in it.
Despite the title to your question, I think you’re actually looking for the minimum dissection into rectangles of a rectilinear polygon. (Jason’s links are about minimum covers by rectangles, which is quite a different problem.)
David Eppstein discusses this problem in section 3 of his 2010 survey article Graph-Theoretic Solutions to Computational Geometry Problems, and he gives a nice summary in this answer on mathoverflow.net:
The idea is to find the maximum number of disjoint axis-parallel diagonals that have two concave vertices as endpoints, split along those, and then form one more split for each remaining concave vertex. To find the maximum number of disjoint axis-parallel diagonals, form the intersection graph of the diagonals; this graph is bipartite so its maximum independent set can be found in polynomial time by graph matching techniques.
Here’s my gloss on this admirably terse description, using figure 2 from Eppstein’s article. Suppose we have a rectilinear polygon, possibly with holes.
When the polygon is dissected into rectangles, each of the concave vertices must be met by at least one edge of the dissection. So we get the minimum dissection if as many of these edges as possible do double-duty, that is, they connect two of the concave vertices.
So let’s draw the axis-parallel diagonals between two concave vertices that are contained entirely within the polygon. (‘Axis-parallel’ means ‘horizontal or vertical’ here, and a diagonal of a polygon is a line connecting two non-adjacent vertices.) We want to use as many of these lines as possible in the dissection as long as they don’t intersect.
(If there are no axis-parallel diagonals, the dissection is trivial—just make a cut from each concave vertex. Or if there are no intersections between the axis-parallel diagonals then we use them all, plus a cut from each remaining concave vertex. Otherwise, read on.)
The intersection graph of a set of line segments has a node for every line segment, and an edge joins two nodes if the lines cross. Here’s the intersection graph for the axis-parallel diagonals:
It’s bipartite with the vertical diagonals in one part, and the horizontal diagonals in the other part. Now, we want to pick as many of the diagonals as possible as long as they don’t intersect. This corresponds to finding the maximum independent set in the intersection graph.
Finding the maximum independent set in a general graph is an NP-hard problem, but in the special case of a bipartite graph, König’s theorem shows that it’s equivalent to the problem of finding a maximum matching, which can be solved in polynomial time, for example by the Hopcroft–Karp algorithm. A given graph can have several maximum matchings, but any of them will do, as they all have the same size. In the example, all the maximum matchings have three pairs of vertices, for example {(2, 4), (6, 3), (7, 8)}:
(Other maximum matchings in this graph include {(1, 3), (2, 5), (7, 8)}; {(2, 4), (3, 6), (5, 7)}; and {(1, 3), (2, 4), (7, 8)}.)
To get from a maximum matching to the corresponding minimum vertex cover, apply the proof of König’s theorem. In the matching shown above, the left set is L = {1, 2, 6, 7}, the right set is R = {3, 4, 5, 8}, and the set of unmatched vertices in L is U = {1}. There is only one alternating path starting in U, namely 1–3–6, so the set of vertices in alternating paths is Z = {1, 3, 6} and the minimum vertex cover is thus K = (L \ Z) ∪ (R ∩ Z) = {2, 3, 7}, shown in red below, with the maximum independent set in green:
Translating this back into the dissection problem, this means that we can use five axis-parallel diagonals in the dissection:
Finally, make a cut from each remaining concave vertex to complete the dissection:
Today I found O(N^5) solution for this problem, and I will share it here.
For the first step, you need to find a way to get the sum of any rectangle in a matrix, with complexity O(1). It's pretty easy to do.
Now for the second step, you need to know dynamic programming. The idea is to store a rectangle and break it into smaller pieces. If the rectangle is empty, you can return 0. And if it's filled, return 1.
There are N^4 states to store the rectangle, plus the O(N) complexity for each state... So you will get an O(N^5) algorithm.
Here's my code. I think it will help.
The input is simple. N, M (size of matrix)
After that, the following N lines will have 1s and 0s.
Example:
4 9
010000010
111010111
101111101
000101000
#include <bits/stdc++.h>
#define MAX 51
int tab[MAX][MAX];
int N,M;
int sumed[MAX][MAX];
int t(int x,int y) {
if(x<0||y<0)return 0;
return sumed[x][y];
}
int subrec(int x1,int y1,int x2,int y2) {
return t(x2,y2)-t(x2,y1-1)-t(x1-1,y2)+t(x1-1,y1-1);
}
int resp[MAX][MAX][MAX][MAX];
bool exist[MAX][MAX][MAX][MAX];
int dp(int x1,int y1,int x2,int y2) {
if(exist[x1][y1][x2][y2])return resp[x1][y1][x2][y2];
exist[x1][y1][x2][y2]=true;
int soma = subrec(x1,y1,x2,y2);
int area = (x2-x1+1)*(y2-y1+1);
if(soma==area){return resp[x1][y1][x2][y2]=1;}
if(!soma) {return 0;}
int best = 1000000;
for(int i = x1;i!=x2;++i) {
best = std::min(best,dp(x1,y1,i,y2)+dp(i+1,y1,x2,y2));
}
for(int i = y1;i!=y2;++i) {
best = std::min(best,dp(x1,y1,x2,i)+dp(x1,i+1,x2,y2));
}
return resp[x1][y1][x2][y2]=best;
}
void backtracking(int x1,int y1,int x2,int y2) {
int soma = subrec(x1,y1,x2,y2);
int area = (x2-x1+1)*(y2-y1+1);
if(soma==area){std::cout<<x1+1<<" "<<y1+1<<" "<<x2+1<<" "<<y2+1<<"\n";return;}
if(!soma) {return;}
int best = 1000000;
int obj = resp[x1][y1][x2][y2];
for(int i = x1;i!=x2;++i) {
int ans = dp(x1,y1,i,y2)+dp(i+1,y1,x2,y2);
if(ans==obj){
backtracking(x1,y1,i,y2);
backtracking(i+1,y1,x2,y2);
return;
}
}
for(int i = y1;i!=y2;++i) {
int ans = dp(x1,y1,x2,i)+dp(x1,i+1,x2,y2);
if(ans==obj){
backtracking(x1,y1,x2,i);
backtracking(x1,i+1,x2,y2);
return;
}
}
}
int main()
{
std::cin >> N >> M;
for(int i = 0; i != N;++i) {
std::string s;
std::cin >> s;
for(int j = 0; j != M;++j) {
if(s[j]=='1')tab[i][j]++;
}
}
for(int i = 0; i != N;++i) {
int val = 0;
for(int j = 0; j != M;++j) {
val += tab[i][j];
sumed[i][j]=val;
if(i)sumed[i][j]+=sumed[i-1][j];
}
}
std::cout << dp(0,0,N-1,M-1) << std::endl;
backtracking(0,0,N-1,M-1);
}
Hello I am fairly new to programming and I am trying, in Java, to create a function that creates recursive triangles from a larger triangles midpoints between corners where the new triangles points are deviated from the normal position in y-value. See the pictures below for a visualization.
The first picture shows the progression of the recursive algorithm without any deviation (order 0,1,2) and the second picture shows it with(order 0,1).
I have managed to produce a working piece of code that creates just what I want for the first couple of orders but when we reach order 2 and above I run into the problem where the smaller triangles don't use the same midpoints and therefore looks like the picture below.
So I need help with a way to store and call the correct midpoints for each of the triangles. I have been thinking of implementing a new class that controls the calculation of the midpoints and stores them and etc, but as I have said I need help with this.
Below is my current code
The point class stores a x and y value for a point
lineBetween creates a line between the the selected points
void fractalLine(TurtleGraphics turtle, int order, Point ett, Point tva, Point tre, int dev) {
if(order == 0){
lineBetween(ett,tva,turtle);
lineBetween(tva,tre,turtle);
lineBetween(tre,ett,turtle);
} else {
double deltaX = tva.getX() - ett.getX();
double deltaY = tva.getY() - ett.getY();
double deltaXtre = tre.getX() - ett.getX();
double deltaYtre = tre.getY() - ett.getY();
double deltaXtva = tva.getX() - tre.getX();
double deltaYtva = tva.getY() - tre.getY();
Point one;
Point two;
Point three;
double xt = ((deltaX/2))+ett.getX();
double yt = ((deltaY/2))+ett.getY() +RandomUtilities.randFunc(dev);
one = new Point(xt,yt);
xt = (deltaXtre/2)+ett.getX();
yt = (deltaYtre/2)+ett.getY() +RandomUtilities.randFunc(dev);
two = new Point(xt,yt);
xt = ((deltaXtva/2))+tre.getX();
yt = ((deltaYtva/2))+tre.getY() +RandomUtilities.randFunc(dev);
three = new Point(xt,yt);
fractalLine(turtle,order-1,one,tva,three,dev/2);
fractalLine(turtle,order-1,ett,one,two,dev/2);
fractalLine(turtle,order-1,two,three,tre,dev/2);
fractalLine(turtle,order-1,one,two,three,dev/2);
}
}
Thanks in Advance
Victor
You can define a triangle by 3 points(vertexes). So the vertexes a, b, and c will form a triangle. The combinations ab,ac and bc will be the edges. So the algorithm goes:
First start with the three vertexes a,b and c
Get the midpoints of the 3 edges p1,p2 and p3 and get the 4 sets of vertexes for the 4 smaller triangles. i.e. (a,p1,p2),(b,p1,p3),(c,p2,p3) and (p1,p2,p3)
Recursively find the sub-triangles of the 4 triangles till the depth is reached.
So as a rough guide, the code goes
findTriangles(Vertexes[] triangle, int currentDepth) {
//Depth is reached.
if(currentDepth == depth) {
store(triangle);
return;
}
Vertexes[] first = getFirstTriangle(triangle);
Vertexes[] second = getSecondTriangle(triangle);
Vertexes[] third = getThirdTriangle(triangle);;
Vertexes[] fourth = getFourthTriangle(triangle)
findTriangles(first, currentDepth+1);
findTriangles(second, currentDepth+1);
findTriangles(third, currentDepth+1);
findTriangles(fourth, currentDepth+1);
}
You have to store the relevant triangles in a Data structure.
You compute the midpoints of any vertex again and again in the different paths of your recursion. As long as you do not change them by random, you get the same midpoint for every path so there's no problem.
But of course, if you modify the midpoints by random, you'll end with two different midpoints in two different paths of recursion.
You could modify your algorithm in a way that you not only pass the 3 corners of the triangle along, but also the modified midpoints of each vertex. Or you keep them in a separate list or map or something and only compute them one time and look them up otherwise.