home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Simtel MSDOS 1992 September
/
Simtel20_Sept92.cdr
/
msdos
/
ddjmag
/
ddj8711.arc
/
TRACER.C
< prev
Wrap
C/C++ Source or Header
|
1987-10-08
|
16KB
|
611 lines
/*-----------------------------------------------------------------------
tracer.c
There is only one externally visible function: find_all_contours
Implements contour search and tracing algorithms
Some sections are based on Pavlidis, "Algorithms for Graphics and
Image Processing"
Quick description: the algorithm examines the interiors of known
contours to find additional contours.
It is started by treating the border of the image itself as a contour.
The final outcome of this algorithm is a tree containing all points
on contours in the image. The key for a leaf in the tree is the point
coordinates. Each leaf also contains an index for the contour containing
the point, and the elevation for the point.
William May
created Jan 31, 1987
modified Apr 10, 1987 improve the trace logic to handle
contours that are multiple pixels in
thickness.
----------------------------------------------------------------------*/
#define DEBUG
#ifdef DEBUG
#define D(x) x
#else
#define D(x)
#endif
#include <QuickDraw.h>
#include <MemoryMgr.h>
#include <stdio.h>
#include "getpixel.h"
#include "contour.h"
#include "global.h"
/* two defines for our trace directions */
#define CLOCKWISE -1
#define COUNTERCLOCKWISE 1
/* the index of the image borders is BASE (the exterior contour) */
#define BASE -1
/* define the queue elements */
typedef struct {
int h, v, chain;
} q_item;
static char *qp; /* pointer to the queue */
static int qmax = 0; /* max items in the queue */
static int current_elevation = 0; /* current elevation of region */
static int next_curve = 0; /* number of next index in curve array */
static long point_count; /* count the points we have found */
/*-----------------------------------------------------------------------
find_all_contours finds all the contours in a bitmap
first the left side of the bitmap is entered into the queue to
start off the search. then each time find_contours returns
(meaning no more contours at that level) the elevation is incremented
for the next round of searches.
-----------------------------------------------------------------------*/
int find_all_contours()
{
char ch;
Point pt;
GrafPtr old_port;
int draw_point();
int item;
register int i;
char *makequeue();
qp = makequeue(4000, sizeof(q_item));
start_queue(); /* set up search for exterior of bitmap */
find_contours(BASE); /* find the first level of contours */
/* parent = -1 */
while (next_point(&item, &pt)) { /* while there is an unsearched curve */
trace(pt, COUNTERCLOCKWISE, true); /* set up queue for next search */
find_contours(item); /* go get 'em. item = parent */
}
free(qp); /* all done, get rid of the queue */
set_elevations(); /* set elevations in the array */
printf("max used in queue is %d\n", qmax);
printf("number of curves found is %d\n", next_curve);
printf("number of points found is %ld\n", point_count);
printf("Curve search complete. <cr> to continue: ");
ch = getchar();
GetPort(&old_port);
SetPort(right_wind);
EraseRect(&(right_wind->portRect));
D(printf("Now traversing the tree.\n"));
D(traverse(root, draw_point));
SetPort(old_port);
}
/*----------------------------------------------------------------------
start_queue puts the left edge of the bitmap into the
queue to act as start for the bitmap search
only the left edge needs to be put in because the other edges
would be ignored anyway
-----------------------------------------------------------------------*/
static start_queue()
{
register int i;
q_item q;
q.h = 0;
q.chain = 6; /* all starting queue items are going south */
for (i = 0; i < VBITMAX; i++) {
q.v = i;
if (!enqueue(&q,qp))
syserr(0, "Queue overflow in startup function\n");
}
}
/*----------------------------------------------------------------------
set the next curve starting point if there is one,
otherwise return 0;
----------------------------------------------------------------------*/
static int next_point(item,pt)
int *item;
Point *pt;
{
static int last_searched = -1; /* last array index used */
if (++last_searched < next_curve) {
*item = last_searched;
pt->h = curves[last_searched].p.h;
pt->v = curves[last_searched].p.v;
return 1;
}
else
return 0;
}
/*----------------------------------------------------------------------
set the elevations in the array
assume all contours are ascending
and increment = 100
----------------------------------------------------------------------*/
set_elevations()
{
register curve_data *p = curves;
register int parent, i;
for (i = 0; i < next_curve; i++, p++) {
if ((parent = curves[i].parent) == BASE)
curves[i].elevation = 100;
else
curves[i].elevation = curves[parent].elevation + 100;
}
}
/*-----------------------------------------------------------------------
find_contours searches for all contours within a closed contour,
based on the queue elements
if any are found a non-zero value is returned
-----------------------------------------------------------------------*/
static int find_contours(parent)
int parent; /* index of enclosing curve */
{
q_item item, *show_next();
register int c;
Point p;
int B, next_chain;
Point first, last;
while (dequeue(&item, qp)) {
c = item.chain;
if (c == 8) {
dequeue(&item, qp);
c = item.chain;
}
if ((c >= 5 && c <= 7) ||
(c == 0 && ((next_chain = (*show_next(qp)).chain) >= 5) &&
(next_chain <= 7))) {
p.h = item.h + 1;
p.v = item.v;
D(first = p);
do {
search_x(&B, &p);
if (B == 1) {
D(last = p);
D(show_line(first, last));
/*
if trace found a new contour then break off
the scan. If the point is a new point on a
known contour, then continue.
*/
if (trace(p, CLOCKWISE, false)) {
/* and add curve to array */
curves[next_curve].p = p;
curves[next_curve].parent = parent;
curves[next_curve].elevation = 0;
curves[next_curve].searched = false;
next_curve++;
break;
} else
p.h++; /* bump up to next point */
}
else if (B >= 2) {
D(last = p);
D(show_line(first, last));
break;
}
} while (1);
}
}
return 1;
}
/*-----------------------------------------------------------------------
scan pixels in the x direction at point p
if the bitmap shows a pixel return 1
if the bitmap shows a pixel and the pixel is in the tree
(i.e. the point has been scanned before) return 2
update the starting point
-----------------------------------------------------------------------*/
static search_x(b, p)
int *b;
Point *p;
{
long dcmp();
/*
the union makes it easy to use the point coordinates
as the key in the tree
*/
union {
Point pt;
long key;
} q;
q.pt = *p;
if (*b = getpixel(q.pt.h, q.pt.v, &bmap)) {
if (find(root, q.key, dcmp)) (*b)++;
return; /* don't increment h on a hit */
}
if (++p->h >= HBITMAX) {
*b = 2; /* when we hit the right edge,indicate already found */
return;
}
}
/*-----------------------------------------------------------------------
trace traces a contour beginning at the specified point
at each found point the x,y coordinates and chain code are
stored both in the queue and the tree.
two result codes are returned:
0 indicates that the starting point represented a new point on an
existing (known) contour. trace was aborted.
1 indicates a new contour was successfully traced
-----------------------------------------------------------------------*/
static int trace(p, dir, q_only)
Point p; /* p is the starting point */
int dir; /* dir is the trace direction */
Boolean q_only; /* trace and put in queue only,
do not check for dupes in tree */
{
Point c, t; /* current point, and transformed point for testing */
int s; /* search direction */
Boolean found, test_pixel();
int count, used, chain;
q_item item;
LEAF *lp, *lp2;
long icmp(), dcmp();
GrafPtr old_port;
union temp {
long key;
Point p;
} temp;
if (dir == COUNTERCLOCKWISE)
s = 6;
else
s = 2;
c = p;
if (q_only) {
/* has this curve interior been searched before? */
temp.p.h = c.h;
temp.p.v = c.v;
lp = find(root, temp.key, dcmp);
if (curves[lp->curve].searched == true) {
printf("Curve %d already searched\n", lp->curve);
return 0;
}
else
curves[lp->curve].searched = true;
item.h = c.h; /* add data to queue */
item.v = c.v;
item.chain = 8; /* mark beginning of new contour */
if (!enqueue(&item,qp))
syserr(0, "Queue overflow in trace\n");
GetPort(&old_port);
SetPort(right_wind);
PenPat(white);
SetPort(old_port);
}
else {
if (!(lp = talloc(sizeof(LEAF))))
syserr(MemErr, "Insufficient memory for AVL tree");
else {
lp->header.p.h = c.h;
lp->header.p.v = c.v;
lp->curve = next_curve;
if (lp2 = insert(&root, lp, icmp)) {
/* tfree(lp); not implemented in getmem! */
return 0; /* starting point already in the tree !! */
}
else { /* if not in the tree then enqueue the new point */
point_count++;
item.h = c.h; /* add data to queue */
item.v = c.v;
item.chain = 8; /* mark beginning of new contour */
if (!enqueue(&item,qp))
syserr(0, "Queue overflow in trace\n");
GetPort(&old_port);
SetPort(right_wind);
PenPat(black);
SetPort(old_port);
}
}
}
do {
found = false;
count = 0;
while (!found && (count < 3)) {
count++;
if (test_pixel(c, add_chain(s, -1 * dir))) {
set_pixel(&c, (chain = add_chain(s, -1 * dir)));
s = add_chain(s, -2 * dir);
found = true;
}
else {
if (test_pixel(c, s)) {
set_pixel(&c, (chain = s));
found = true;
}
else {
if (test_pixel(c, add_chain(s, 1 * dir))) {
set_pixel(&c, (chain = add_chain(s, 1 * dir)));
found = true;
}
else
s = add_chain(s, 2 * dir);
}
}
}
D(show_point(c));
if (q_only) {
item.h = c.h; /* add data to queue */
item.v = c.v;
item.chain = chain;
if (!enqueue(&item,qp))
syserr(0, "Queue overflow in trace\n");
}
else {
if (!(lp = talloc(sizeof(LEAF))))
syserr(MemErr, "Insufficient memory for AVL tree");
else {
lp->header.p.h = c.h;
lp->header.p.v = c.v;
lp->curve = next_curve;
if (lp2 = insert(&root, lp, icmp)) {
/* tfree(lp); not implemented in getmem! */
if ((c.h == p.h) && (c.v == p.v))
return 1; /* we have returned to the start */
else
return 0;
}
else { /* if not in the tree then enqueue point */
point_count++;
item.h = c.h; /* add data to queue */
item.v = c.v;
item.chain = chain;
if (!enqueue(&item,qp))
syserr(0, "Queue overflow in trace\n");
}
}
}
if ((used = sp_used(qp)) > qmax) qmax = used;
} while ((c.h != p.h) || (c.v != p.v));
GetPort(&old_port);
SetPort(right_wind);
PenPat(black);
SetPort(old_port);
return 1;
}
/*---------------------------------------------------------------------
test the s neighbor of point p
---------------------------------------------------------------------*/
static Boolean test_pixel(p,s)
Point p;
int s; /* direction to test */
{
set_pixel(&p, s);
return (getpixel(p.h, p.v, &bmap));
}
/*----------------------------------------------------------------------
set point p to point + chain code s
----------------------------------------------------------------------*/
static set_pixel(p, s)
Point *p;
int s;
{
switch (s) {
case 0:
(*p).h++;
break;
case 1:
(*p).h++;
(*p).v--;
break;
case 2:
(*p).v--;
break;
case 3:
(*p).h--;
(*p).v--;
break;
case 4:
(*p).h--;
break;
case 5:
(*p).h--;
(*p).v++;
break;
case 6:
(*p).v++;
break;
case 7:
(*p).h++;
(*p).v++;
break;
}
}
/*----------------------------------------------------------------------
add c to chain code s
----------------------------------------------------------------------*/
static int add_chain(s,c)
register int s,c;
{
register int i;
if ((i = s + c) >= 8)
return (i - 8);
else {
if (i < 0)
return (i + 8);
else
return i;
}
}
#ifdef DEBUG
/*-----------------------------------------------------------------------
The code below is used to display the progress of the contour
analysis in a Macintosh window. It is obviously totally
nonportable. Fixed point arithmetic is used for scaling the MacPaint
size document to a much smaller window. Fixed point is much faster
floating point, and is often used for two and three dimensional
graphics on the Mac. This code is for debugging purposes.
-----------------------------------------------------------------------*/
/* ratio is used to scale down picture size by 1/4 */
static Fixed ratio = 0x00004000;
/*-----------------------------------------------------------------------
convert a 16 bit integer to fixed point
-----------------------------------------------------------------------*/
static Fixed int_to_fix(i)
int i;
{
asm {
move.w i,d0 ; get the number
ext.l d0 ; clear top of d0
swap d0 ; make it a fixed point number, all done
}
}
/*-----------------------------------------------------------------------
convert a Fixed point number to 16 bit integer
-----------------------------------------------------------------------*/
static int fix_to_int(i)
Fixed i;
{
asm {
move.l i,d0 ; get the number
add.l #0xA000,d0 ; add .5 to number, for rounding
swap d0 ; make the int, ignore upper word of d0
}
}
/*-----------------------------------------------------------------------
draw the point from a LEAF, scaled down to the actual window size
-----------------------------------------------------------------------*/
static draw_point(p)
LEAF *p;
{
GrafPtr oldPort;
Point q;
Rect r;
q = p->header.p;
GetPort(&oldPort);
SetPort(right_wind);
r.top = fix_to_int(FixMul(int_to_fix(q.v), ratio));
r.left = fix_to_int(FixMul(int_to_fix(q.h), ratio));
r.right = r.left + 1;
r.bottom = r.top + 1;
FrameRect(&r);
SetPort(oldPort);
}
/*-----------------------------------------------------------------------
draw the point, scaled down to the actual window size
-----------------------------------------------------------------------*/
static show_point(p)
Point p;
{
GrafPtr oldPort;
Rect r;
GetPort(&oldPort);
SetPort(right_wind);
r.top = fix_to_int(FixMul(int_to_fix(p.v), ratio));
r.left = fix_to_int(FixMul(int_to_fix(p.h), ratio));
r.right = r.left + 1;
r.bottom = r.top + 1;
FrameRect(&r);
SetPort(oldPort);
}
/*-----------------------------------------------------------------------
draw a line, scaled down to the actual window size
-----------------------------------------------------------------------*/
static show_line(first, last)
Point first,last;
{
GrafPtr oldPort;
GetPort(&oldPort);
SetPort(right_wind);
first.v = fix_to_int(FixMul(int_to_fix(first.v), ratio));
first.h = fix_to_int(FixMul(int_to_fix(first.h), ratio));
MoveTo(first.h, first.v);
last.v = fix_to_int(FixMul(int_to_fix(last.v), ratio));
last.h = fix_to_int(FixMul(int_to_fix(last.h), ratio));
LineTo(last.h, last.v);
SetPort(oldPort);
}
#endif