/* * revfit.c : edge reconstruction and the inverse process. */ #include #include "revfit.h" #ifndef abs #define abs(a) ((a)>=0 ? (a) : -(a) ) #endif #define HalfSUBPIXRES (SUBPIXRES/2) #define ESTABLISHED 127 #define MAXRUN 2000 /* max no of pixel edges in a line */ extern DrawPixelEdge(int x, int y, int V_H); /* a user supplied function */ /* for drawing a PixelEdge */ /**********************************************************************\ * typedef's for sub-pixel resolution pixel edges and gradient bounds * \**********************************************************************/ typedef struct { int x1,y1; /* from (coordinates multiplied by sub-pixel resolution) */ int x2,y2; /* to (coordinates multiplied by sub-pixel resolution) */ } Pedge; typedef struct { int ly,lx; /* lower limit */ int uy,ux; /* upper limit */ } Bound; #define MidX(e) (((e).x1+(e).x2)/2) /* midpt coordinates of a Pedge */ #define MidY(e) (((e).y1+(e).y2)/2) #define Is_Horizontal(d) (abs(d)==HRZ) /* a horizontal direction? (1, -1) */ #define Is_Vertical(d) (abs(d)==VRT) /* a vertical direction? (2, -2) */ #define against(a,b) (!((a)+(b))) /* whether two directions are opp. */ #define Bound_OK(b) (slopecmp((b).uy,(b).ux,(b).ly,(b).lx)) #define WithinBound(dy,dx,b) (slopecmp((dy),(dx),(b).ly,(b).lx) &&\ slopecmp((b).uy,(b).ux,(dy),(dx))) /*************************************************************************** * Get_Pedge(): Returns a pointer to the current Pedge from the list el. * * The position of the cursor of list is not modified. * * Returns NULL if no more edges in the list. * * Coordinates multiplied by sub-pixel resolution. * ***************************************************************************/ static Pedge *Get_Pedge(Edgelist el) { static Pedge e; int dir; if (el.current>=el.Nedges) return NULL; if (Is_Horizontal(dir=(el.list[el.current].dir))) { e.y1=e.y2=el.list[el.current].y*SUBPIXRES + HalfSUBPIXRES; e.x1=el.list[el.current].x*SUBPIXRES - (dir>0 ? HalfSUBPIXRES : -HalfSUBPIXRES); e.x2=e.x1 + (dir>0 ? SUBPIXRES : -SUBPIXRES); } else { e.x1=e.x2=el.list[el.current].x*SUBPIXRES + HalfSUBPIXRES; e.y1=el.list[el.current].y*SUBPIXRES - (dir>0 ? HalfSUBPIXRES : -HalfSUBPIXRES); e.y2=e.y1 + (dir>0 ? SUBPIXRES : -SUBPIXRES); } return &e; } /* Get_Pedge() */ /************************************************************************ * forward(): Update the cursor of the list to the next edge. * ************************************************************************/ #define forward(el) (((el).current)++) /************************************************************************* * backward(): Move back the cursor of the list one place so that * * the previous edge can be visited again. * *************************************************************************/ #define backward(el) (((el).current)--) /************************************************\ * wayof(): return a direction. * \************************************************/ /* the directions no.s are chosen s.t. d1==-d2 if d1,d2 are opp. */ static int wayof(Pedge e) { int d=e.x2-e.x1; return d ? d/SUBPIXRES /* 1 or -1 for horizontal edge */ : (e.y2 - e.y1)/HalfSUBPIXRES; /* 2 or -2 for vertical edge */ } /* wayof() */ /************************************************************************** * slopecmp(): True if grad vector of the 1st is on the counter-clockwise * * side of the 2nd one * **************************************************************************/ static int slopecmp(int dy1,int dx1, int dy2,int dx2) { return (long)dx2*dy1 > (long)dx1*dy2; } /* slopecmp() */ /***************************************************************************\ * calcbound(): calc the bounds (the pair of gradient limits) for the Pedge * \***************************************************************************/ void calcbound(int dominantdir, Pedge e, int Sx, int Sy, Bound* b, IntPoint2 *gradU, IntPoint2 *gradL) { /* gradU and gradL shall be filled with the gradients just within the limits */ int dy,dx; if (Is_Horizontal(dominantdir)) { /* horizontal dominant direction */ b->uy = (e.y1+e.y2+SUBPIXRES)/2-Sy; b->ux = (e.x1+e.x2)/2 -Sx; b->ly = (e.y1+e.y2-SUBPIXRES)/2-Sy; gradU->x = gradL->x = b->lx = b->ux; gradU->y = b->uy-1; gradL->y = b->ly+1; } else { /* up or down dominant direction */ b->uy = (e.y1+e.y2)/2 -Sy; b->ux = (e.x1+e.x2+SUBPIXRES)/2-Sx; gradU->y = gradL->y = b->ly = b->uy; b->lx = (e.x1+e.x2-SUBPIXRES)/2-Sx; gradU->x = b->ux-1; gradL->x = b->lx+1; } if (!Bound_OK(*b)) { /* swaps the bounds if necessary */ IntPoint2 p; dx=b->ux; dy=b->uy; b->ux=b->lx; b->uy=b->ly; b->lx=dx; b->ly=dy; p=*gradU; *gradU=*gradL; *gradL=p; } } /* calcbound() */ /****************************************************************************** * fitlines() : The reversible straight line edge reconstruction routine * ******************************************************************************/ int fitlines(Edgelist el, boolean Pretest, boolean TryAllEndPts, IntPoint2 *lines, int MaxNLine) { /*----------------------------------------------------------------------------* * el : The supplied list of PixelEdges. * Pretest : 1=perform pre-test on each pixel edge, i.e., stop as soon as * a valid end pt cannot be found on a pixel edge. * 0=Allows stepping back. * TryAllEndPts: 1=Try all possible end-pts, 0=Use the one closest to mid-pt. * lines[] : A preallocated array to be filled with end pts of fitted lines * Note: Coordinates of the end pts are multiplied by SUBPIXRES. * MaxNLine : The size of the lines[] array. *----------------------------------------------------------------------------*/ int i,linescount,startp,Nendpt,Nstartpt,NPedges,Nbound; /* counters */ int Sx,Sy,Ex,Ey, Ux,Uy,Lx,Ly, maindir,trnsvrse,dnow, ndir,dir[3]; flag breaktrace, starttrace; /* flags */ int currentsave, bestpt, maxlen, bestpt_currentsave, bestpt_Nendpt; IntPoint2 startpts[SUBPIXRES],endlist[SUBPIXRES],bestpt_endlist[SUBPIXRES]; Pedge Pedgehistory[MAXRUN],e,last,*nextp,estartsave,bestpt_last; Bound bound[MAXRUN]; el.current=0; /* set cursor to the first edge */ e = *Get_Pedge(el); /* first edge */ Sx = MidX(e); Sy = MidY(e); if (!TryAllEndPts) { lines[0].x = Sx; /* record the 1st starting pt. */ lines[0].y = Sy; linescount=1; } else { flag hori = Is_Horizontal(wayof(e)); Nstartpt=0; startpts[0].x = Sx; startpts[0].y = Sy; for (i=1;imaxlen) { /* find all possible end pts only if length longer than maxlen so far */ int h,addy,addx; if (abs(maindir)==1) { addy=1; addx=0; } else {addy=0; addx=1;} if (WithinBound(dy,dx,b)) { /* check mid-pt first */ endlist[0].x=Ex; endlist[0].y=Ey; Nendpt=1; } else Nendpt=0; for (h=1; h0; } else { /* TryAllEndPts==FALSE. just calc the pt closest to the mid-pt */ if (!slopecmp(dy,dx,b.ly,b.lx)) { /* * dy dx is equal or below the lower limit. * i.e. the slope just above the lower limit should be taken. * if the lower gradient limit hits exactly on a sub-pixel res point, * the truncation of the integer division has done part of the job. */ if (Is_Horizontal(maindir)) { tmp= dx*b.ly; exact= (dx==0 || tmp%b.lx==0); Ey = tmp/b.lx + Sy + (b.lx>0 ? (b.ly>0 ? 1 : exact) : (b.ly>0 ? -exact : -1 )); } else { tmp= dy*b.lx; exact= (dy==0 || tmp%b.ly==0); Ex = tmp/b.ly + Sx + (b.ly>0 ? (b.lx>0 ? -exact : -1 ) : (b.lx>0 ? 1 : exact)); } EndptOK = Pretest || WithinBound(Ey-Sy,Ex-Sx,b); } else if (!slopecmp(b.uy,b.ux,dy,dx)) { /* * dy dx is equal or above the upper limit. * i.e. the slope just below the upper limit should be taken. * if the upper gradient limit hits exactly on a sub-pixel res point, * the truncation of the integer division has done part of the job. */ if (Is_Horizontal(maindir)) { tmp= dx*b.uy; exact= (tmp%b.ux==0); Ey = tmp/b.ux + Sy + (b.ux>0 ? (b.uy>0 ? -exact :-1 ) : (b.uy>0 ? 1 : exact)); } else { tmp= dy*b.ux; exact= (tmp%b.uy==0); Ex = tmp/b.uy + Sx + (b.uy>0 ? (b.ux>0 ? 1 : exact) : (b.ux>0 ? -exact :-1 )); } EndptOK = Pretest || WithinBound(Ey-Sy,Ex-Sx,b); } else /* dy,dx is within the limits. i.e. mid-point is taken. */ EndptOK=1; } /* if (TryAllEndPts)..else.. */ if (EndptOK) break; /* if Pretest is TRUE, EndptOK always TRUE */ else { /* no valid end-point can be found, step back one edge */ backward(el); last = Pedgehistory[--NPedges-2]; Nbound--; } } /* for (;;) */ /* until a valid end pt is found */ breaktrace=TRUE; /* one line segment found. */ } else { /* limits not crossed over yet */ Nbound++; /* one more new valid bound */ continue; /* continue to get another Pedge */ } /* if (various trace breaking conditions) */ } /* if (nextp==NULL || ndir==ESTABLISHED) */ else { /* i.e. dominant and trnsvrse direction not yet established */ breaktrace = FALSE; if (ndir<3) { for (i=0;i0 ? -HalfSUBPIXRES : HalfSUBPIXRES) - Sx; Ux = Lx + (maindir>0 ? SUBPIXRES : -SUBPIXRES); Uy = Ly = MidY(e) - Sy; } else { Ly = e.y1 + (maindir>0 ? -HalfSUBPIXRES : HalfSUBPIXRES) - Sy ; Uy = Ly + (maindir>0 ? SUBPIXRES : -SUBPIXRES); Ux = Lx = MidX(e) - Sx; } } if (slopecmp(Ly,Lx,Uy,Ux)) { /* swap the grad limits if necessary */ bound[0].uy=Ly; bound[0].ux=Lx; /* Ly Lx larger */ bound[0].ly=Uy; bound[0].lx=Ux; } else { bound[0].uy=Uy; bound[0].ux=Ux; /* Uy Ux larger */ bound[0].ly=Ly; bound[0].lx=Lx; } Nbound=1; /* first bound established */ ndir = ESTABLISHED; } /* if (ndir==3) */ } /* if (ndir==ESTABLISHED)...else... */ /*--------------------------*/ if (breaktrace) { /* one line ended */ /*--------------------------*/ backward(el); /* last pixel edge shall be the start of another line. */ if (TryAllEndPts) { if (maxlen < (el.current-currentsave)) { /* longer than the longest */ maxlen = el.current-currentsave; /* longest distance so far */ bestpt_last=last; /* save the last edge */ bestpt=startp; /* update the best pt so far*/ bestpt_currentsave=el.current; /* save the cursor for el */ for (i=0; i= Nstartpt) { /* all starting pts have been tried */ currentsave=el.current=bestpt_currentsave; /* save the ending pos */ estartsave=e=bestpt_last; /* save the ending Pedge */ lines[linescount++] = startpts[bestpt]; /* record the best pt */ if (linescount>=MaxNLine) return -1; /* too many lines */ if (bestpt_currentsave>=el.Nedges-1) { /* no more Pixel edges ? */ lines[linescount++]=bestpt_endlist[0]; /* record end pt as well */ return linescount>=MaxNLine ? -1 : linescount;/* done */ } Nstartpt=bestpt_Nendpt; /* use the list of end pts as starting pts */ for (i=0; i=Nstartpt) ... else ... */ } else { /* i.e. TryAllEndPts==FALSE. simply start at the end pt again */ Sx=Ex; Sy=Ey; e=last; lines[linescount].x=Ex; lines[linescount++].y=Ey; if (linescount>=MaxNLine) return -1; /* too many lines */ if (el.current>=el.Nedges-1) return linescount;/* no more Pedges, done */ } starttrace=TRUE; /* start again */ } /* if (breaktrace) */ } /* for (starttrace=TRUE;;) infinite loop */ } /* fitlines() */ /***********************************************************************\ * T H E I N V E R S E P R O C E S S * \***********************************************************************/ #define divisible(a,b) ((a)%(b)==0) #define ishori(x,y) (divisible(x,SUBPIXRES)||\ divisible(y+HalfSUBPIXRES,SUBPIXRES)) #define isvert(x,y) (divisible(y,SUBPIXRES)||\ divisible(x+HalfSUBPIXRES,SUBPIXRES)) #define sign(x) ((x)>=0 ? 1 : -1) #define Trunc(n) ((n)/SUBPIXRES*SUBPIXRES) static int lastx,lasty,lastdir; /* to avoid duplicated pixel edges */ static void drawHPedge(int x, int y) { /* draw a horizontal pixel edge */ if (lastx==x && lasty==y && lastdir==HRZ) /* starting edge==last ending edge */ return; lastx=x; lasty=y; lastdir=HRZ; DrawPixelEdge(x/SUBPIXRES, y/SUBPIXRES, HRZ); /* call the user function */ } /* drawHPedge() */ static void drawVPedge(int x, int y) { /* draw a vertical pixel edge */ if (lastx==x && lasty==y && lastdir==VRT) /* starting edge==last ending edge */ return; lastx=x; lasty=y; lastdir=VRT; DrawPixelEdge(x/SUBPIXRES, y/SUBPIXRES, VRT); /* call the user function */ } /* drawVPedge() */ /************************************************************************** * makejaggedline(): A modified Bresenham's mid-point algorithm. Based on * * the code from the original Graphics Gem. Neither the starting pt * * nor the ending pt need to be at the mid-pt of a pixel edge. * * The decision variable has been scaled by SUBPIXRES and preloaded * * with the offset from a `proper' starting pt, i.e. the mid-pt of the * * first pixel edge pointing to the dominant direction. * **************************************************************************/ static makejaggedline(int x1, int y1, int x2, int y2) { int d, x, y, ax, ay, sx, sy, dx, dy, finaltrnsvrse; dx = x2-x1; ax = abs(dx)*SUBPIXRES; sx = sign(dx)*SUBPIXRES; dy = y2-y1; ay = abs(dy)*SUBPIXRES; sy = sign(dy)*SUBPIXRES; /*============*/ if (ax>ay) /* x dominant */ { /*============*/ if (isvert(x1,y1)) /* 1st edge is trnsvrse. skip to the mid-pt */ { /* of the next dominant dir edge. */ y=Trunc(y1 + HalfSUBPIXRES) + sy/2; x=Trunc(x1) + HalfSUBPIXRES + sx/2; drawVPedge(x-sx/2,y-sy/2); /* draw the skipped edge */ } else { /* 1st edge is dominant. shift to the mid-pt */ x=Trunc(x1 + HalfSUBPIXRES); y=Trunc(y1) + HalfSUBPIXRES; } /* preload decision var `d' with offset x-x1, y-y1. (if any) */ d = ay - (ax>>1) + ay*(x-x1)/sx - ax*(y-y1)/sy; for (;;) { drawHPedge(x,y); if (abs(x-x2) < HalfSUBPIXRES) return; /* final edge is a dominant one */ x += sx; finaltrnsvrse = dx>0 ? x>x2: x0 || finaltrnsvrse) { /* if the final edge is a trnsvrse */ drawVPedge(x-sx/2,y+sy/2); /* one, draw it before stopping */ y += sy; d -= ax; } if (finaltrnsvrse) return; d += ay; } /* for (;;) */ } /*============*/ else /* y dominant */ { /*============*/ if (ishori(x1,y1)) /* 1st edge trnsvrse. skip to the mid-pt */ { /* of the next dominant dir edge */ x=Trunc(x1 + HalfSUBPIXRES) + sx/2; y=Trunc(y1) + HalfSUBPIXRES + sy/2; drawHPedge(x-sx/2, y-sy/2); /* draw the skipped edge */ } else { /* 1st edge is dominant. shift to the mid-pt */ x=Trunc(x1) + HalfSUBPIXRES; y=Trunc(y1 + HalfSUBPIXRES); } /* preload decision var `d' with offset x-x1, y-y1 (if any) */ d = ax - (ay>>1) + ax*(y-y1)/sy - ay*(x-x1)/sx; for (;;) { drawVPedge(x,y); if (abs(y-y2) < HalfSUBPIXRES) return; /* final edge is a dominant one */ y += sy; finaltrnsvrse = dy>0 ? y>y2 : y0 || finaltrnsvrse) { /* if the final one is a trnsvrse */ drawHPedge(x+sx/2, y-sy/2); /* one, draw it before stopping. */ x += sx; d -= ay; } if (finaltrnsvrse) return; d += ax; } /* for (;;) */ } /* if (ax>ay)... else ...*/ } /* makejaggedline() */ /*************************************************************************** * linestojagged(): reconstruct a sequence of pixel edges from given lines * * by calling the makejaggedline() function. * ***************************************************************************/ void linestojagged(int Nlines, IntPoint2 *lines) { int from_x, from_y, i; lastdir=0; for (from_x=lines[0].x, from_y=lines[0].y, i=1; i