/*
 *   Image Processing Library
 *   Thomas Braunl, Univ. Stuttgart, 1996
 *   Additional Code by Gerrit Heitsch
 *   Adapted by Michael Rudolph
 *   derived after Braunl et al. : Parallele Bildverarbeitung, Addison-Wesley
 *
 *   $Id: improc.c,v 1.8 1996/07/04 12:27:47 rudolpml Exp $
 *
 */

#define IMPLEMENT_IPL

#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "improc.h"

/*
 *
 */

static int  width;
static int  height;
static BYTE markerDone;
static BYTE markerContour;
static BYTE black;
static BYTE white;
static BYTE h1[MAX_IMAGE_SIZE];


static int CheckCircle(int length, IPLPoint * contour, IPLPoint * cog);
static int IsEdge(BYTE * image, int x, int y, int w, BYTE threshold);
static int FollowContour(BYTE * imageIn,
                         int x,
                         int y,
                         BYTE threshold,
                         IPLPoint * points,
                         int maxLength);




/*
 *    IPL_init(IPL_CONFIG * config)
 *
 *    initialisation routine of the IPL. Set the image width and height,
 *    the byte codes for the colors black and white.
 */

void IPL_init(IPL_CONFIG * config)
{
    width  = config -> imageWidth;
    height = config -> imageHeight;
    black  = config -> colorBlack;
    white  = config -> colorWhite;
    markerDone = white + 1;
    markerContour = white;
}



void IPL_col2gray(BYTE * imageIn, BYTE * imageOut)
{
    int i;
    for (i = width; i < (height-1)*width; i++)
      imageOut[i] = (imageIn[3*i] + imageIn[3*i+1] + imageIn[3*i+2]) / (3*16);
}


void IPL_matchcol(BYTE * imageIn, BYTE * imageOut, BYTE r, BYTE g, BYTE b)
{   /* square color distance */
    int i, sqdiff;

    for (i = width; i < (height-1)*width; i++)
    {
      sqdiff = abs(imageIn[3*i]   - r) * abs(imageIn[3*i]   - r)
             + abs(imageIn[3*i+1] - g) * abs(imageIn[3*i+1] - g)
             + abs(imageIn[3*i+2] - b) * abs(imageIn[3*i+2] - b);
      /* max diff = 3 * 255*255 */
      imageOut[i] = (BYTE) (15 - 15 * sqdiff / (3*255*255));
    }
}



void IPL_laplace(BYTE * imageIn, BYTE * imageOut)
{
    int i;
    int delta;

    for (i = width; i < (height-1)*width; i++)
    {
        delta  = abs(4 * imageIn[i] -
                         imageIn[i-1] -
                         imageIn[i+1] -
                         imageIn[i-width] -
                         imageIn[i+width]);

        if (delta > white)
            imageOut[i] = white;
        else
            imageOut[i] = (BYTE)delta;
    }
}

void IPL_TestPalette(BYTE * imageOut)
{
    int dist = 15;
    int i,j,pos;
    float facx,facy;

    facy = 255.0 / (float) height;
    facx = 255.0 / (float) width;
    pos = 0;
    printf("r/g/b black/white");
    for (i = 0; i < height; i++)
    for (j = 0; j < width; j++)
    {
       if (i == dist)
       { imageOut[pos++] = 255;  /* red */
         imageOut[pos++] = 0;    /* green */
         imageOut[pos++] = 0;    /* blue */
       }
       else
       if (i == 2*dist)
       { imageOut[pos++] = 0;    /* red */
         imageOut[pos++] = 255;  /* green */
         imageOut[pos++] = 0;    /* blue */
       }
       else
       if (i == 3*dist)
       { imageOut[pos++] = 0;    /* red */
         imageOut[pos++] = 0;    /* green */
         imageOut[pos++] = 255;  /* blue */
       }
       else
       if (i == 5*dist)
       { imageOut[pos++] = 0;    /* red */
         imageOut[pos++] = 0;    /* green */
         imageOut[pos++] = 0;    /* blue */
       }
       else
       if (i == 6*dist)
       { imageOut[pos++] = 255;  /* red */
         imageOut[pos++] = 255;  /* green */
         imageOut[pos++] = 255;  /* blue */
       }
       else
       { imageOut[pos++] = (int) (facx * j);  /* red */
         imageOut[pos++] = (int) (facy * i);  /* green */
         imageOut[pos++] = 0;  /* blue */
       }
    }
}


void IPL_noise(BYTE * imageIn, BYTE * imageOut, double noise)
{
    int i;
    int imageSize = width * height;
    int numberOfPixels = (int)(imageSize * noise);

    memcpy(imageOut, imageIn, imageSize);

    for (i = 0; i < numberOfPixels; i+=2)
    {
       imageOut[rand() % imageSize] = white;
       imageOut[rand() % imageSize] = black;
    }
}


void IPL_Cnoise(BYTE * imageIn, BYTE * imageOut, double noise)
{
    int i,pos;
    int imageSize = width * height;
    int numberOfPixels = (int)(imageSize * noise);

    memcpy(imageOut, imageIn, 3*imageSize);

    for (i = 0; i < numberOfPixels; i+=2)
    {
       pos = rand() % imageSize;
       imageOut[3*pos  ] = ColWhite;
       imageOut[3*pos+1] = ColWhite;
       imageOut[3*pos+2] = ColWhite;
       pos = rand() % imageSize;
       imageOut[3*pos  ] = ColBlack;
       imageOut[3*pos+1] = ColBlack;
       imageOut[3*pos+2] = ColBlack;
    }
}

void IPL_difference(BYTE * image1, BYTE * image2, BYTE * imageOut)
{
    int i;
    int delta;

    for (i = 0; i < width * height; i++)
    {
        delta = image1[i] - image2[i];
        if (delta < 0) delta = - delta;
        imageOut[i] = (BYTE)delta;
    }
}

void IPL_Cdifference(BYTE * image1, BYTE * image2, BYTE * imageOut)
{
    int i;
    int delta;

    for (i = 0; i < 3*width * height; i++)
    {
        delta = image1[i] - image2[i];
        if (delta < 0) delta = - delta;
        imageOut[i] = (BYTE)delta;
    }
}

void IPL_sobel(BYTE *imageIn, BYTE *imageOut)
{
    int i;
    int grad;
    int deltaX, deltaY;

    memset(imageOut, 0, width);

    for (i = width; i < (height-1)*width; i++)
    {
        deltaX = 2*imageIn[i+1] + imageIn[i-width+1] + imageIn[i+width+1] -
                 2*imageIn[i-1] - imageIn[i-width-1] - imageIn[i+width-1];


        deltaY = imageIn[i-width-1] + 2*imageIn[i-width] + imageIn[i-width+1] -
                 imageIn[i+width-1] - 2*imageIn[i+width] - imageIn[i+width+1];

        grad = (abs(deltaX) + abs(deltaY)) / 3;
        if (grad > white)
            grad = white;

        imageOut[i] = (BYTE)grad;
    }

    memset(imageOut + i, 0, width);
}


void IPL_mean(BYTE *imageIn, BYTE *imageOut)
{
    int i;

    memset(imageOut, 0, width);

    for (i = width; i < (height-1)*width; i++)
    {
        imageOut[i] = (BYTE)(imageIn[i-width-1] +
                             imageIn[i-width]   +
                             imageIn[i-width+1] +
                             imageIn[i-1]       +
                             imageIn[i]         +
                             imageIn[i+1]       +
                             imageIn[i+width-1] +
                             imageIn[i+width]   +
                             imageIn[i+width+1]      )  / 9;
    }

    memset(imageOut + i, 0, width);
}

void IPL_Cmean(BYTE *imageIn, BYTE *imageOut)
{
    int i;
    memset(imageOut, 0, 3*width);

    for (i = width; i < 3*(height-1)*width; i++)
    {
        imageOut[i] = (BYTE)(imageIn[i-3*(width-1)] +
                             imageIn[i-3*(width)]   +
                             imageIn[i-3*(width+1)] +
                             imageIn[i-3]       +
                             imageIn[i]         +
                             imageIn[i+3]       +
                             imageIn[i+3*(width-1)] +
                             imageIn[i+3*(width)]   +
                             imageIn[i+3*(width+1)]   )  / 9;
    }
    memset(imageOut + i, 0, width);
}


void IPL_median(BYTE * imageIn, BYTE * imageOut)
{
    int i;
    BYTE values[9];

    memset(imageOut, 0, width);

    /*
     *    loop over the image, starting at the second row, second column
     */

    for (i = width; i < width * (height - 1); i++)
    {
        int x, y;
        int k = 0, l;
        int j;

        /*
         *    sort the 3x3 environment by simple
         *    insertion sort.
         */

        for (y = -1; y <= 1; y++)
	{
            for (x = -1; x <= 1; x++)
	    {
                BYTE val = imageIn[i + y * width + x];

                for (j = 0; j < k; j++)
                    if (val < values[j])
                        break;

                for (l = k; l > j; l--)
                    values[l] = values[l-1];
                values[j] = val;
                k++;
            }

            imageOut[i] = values[4];
	}
    }

    memset(imageOut + i, 0, width);
}

void IPL_min(BYTE *imageIn, BYTE *imageOut)
{
    int i;
    int val;
    int x, y;

    memset(imageOut, 0, width);

    for (i = width; i < (height-1)*width; i++)
    {
        val = white;

        for (y = -1; y <= 1; y++)
            for (x = -1; x <= 1; x++)
                if (imageIn[i + x + y * width] < val)
                    val = imageIn[i + x + y * width];

        imageOut[i] = (BYTE)val;
    }

    memset(imageOut + i, 0, width);
}

void IPL_max(BYTE *imageIn, BYTE *imageOut)
{
    int i;
    int val;
    int x, y;

    memset(imageOut, 0, width);

    for (i = width; i < (height-1)*width; i++)
    {
        val = black;

        for (y = -1; y <= 1; y++)
            for (x = -1; x <= 1; x++)
                if (imageIn[i + x + y * width] > val)
                    val = imageIn[i + x + y * width];

        imageOut[i] = (BYTE)val;
    }

    memset(imageOut + i, 0, width);
}


void IPL_identity(BYTE * imageIn, BYTE * imageOut)
{
    memcpy(imageOut, imageIn, width * height);
}

void IPL_Cidentity(BYTE * imageIn, BYTE * imageOut)
{
    memcpy(imageOut, imageIn, 3*width * height);
}


void IPL_threshold(BYTE * imageIn, BYTE * imageOut, BYTE threshold)
{
    int i;

    for (i = 0; i < width * height; i++)
    {
        if (imageIn[i] >= threshold)
            imageOut[i] = white;
        else
            imageOut[i] = black;
    }
}


void IPL_negation(BYTE * imageIn, BYTE * imageOut)
{
    int i;

    for (i = 0; i < width * height; i++)
        imageOut[i] = white - imageIn[i];
}

void IPL_Cnegation(BYTE * imageIn, BYTE * imageOut)
{
    int i;

    for (i = 0; i < 3*width * height; i++)
        imageOut[i] = ColWhite - imageIn[i];
}


void IPL_gray_stretch(BYTE * imageIn, BYTE * imageOut)
{
    int i;
    BYTE maxVal = black;
    BYTE minVal = white;
    BYTE range;
    double factor;

    for (i = 0; i < width*height; i++)
    {
        if (imageIn[i] < minVal)
            minVal = imageIn[i];

        if (imageIn[i] > maxVal)
            maxVal = imageIn[i];   
    }

    range = maxVal - minVal;
    if (range == 0)
        range = 1;
    factor = (double)white / range;

    for (i = 0; i < width*height; i++)
        imageOut[i] = (BYTE)(factor * (imageIn[i] - minVal));
}


void IPL_gray_reduce(BYTE * imageIn, BYTE * imageOut, int numvals)
{
    int i;
    BYTE maxVal = black;
    BYTE minVal = white;
    BYTE range;

    for (i = 0; i < width*height; i++)
    {
        if (imageIn[i] < minVal) minVal = imageIn[i];
        if (imageIn[i] > maxVal) maxVal = imageIn[i];   
    }

    range = maxVal - minVal;

    if (range + 1 > numvals)
    {
        for (i = 0; i < width*height; i++)
        {
            int temp = ((numvals-1) * (imageIn[i] - minVal) / range);
            imageOut[i] = (BYTE)((range * temp / (numvals - 1)) + minVal);
        }
    }
    else
        memcpy(imageOut, imageIn, width * height);
}

void IPL_gen_histogram(BYTE * imageIn, int * histogram)
{
    int i;

    memset(histogram, 0, (white - black + 1) * sizeof(int));
    for (i = 0; i < width*height; i++)
        histogram[imageIn[i]]++;
}

void IPL_equal_histogram(BYTE * imageIn, BYTE * imageOut, int *histogram)
{
    int i, j;
    int sum = 0;

    memset(histogram, 0, white * sizeof(int));

    for (j = black; j <= white; j++)
    {
        for (i = 0; i < width*height; i++)
        {
            if (imageIn[i] == j)
                imageOut[i] = (white * sum / (white - black));  
        }

        sum++;
        histogram[j]++;
    }
}

void IPL_erosion (BYTE * imageIn, BYTE * imageOut, char * struc)
{
    int x, y;
    int offset;

    for (y = 1; y < height - 1; y++)
    {
        for (x = 1; x < width - 1; x++)
	{
            offset = x + y * width;

        if ((struc[0] == 1 && imageIn[offset-1 - width] == white) ||
            (struc[1] == 1 && imageIn[offset   - width] == white) ||
            (struc[2] == 1 && imageIn[offset+1 - width] == white) ||
            (struc[3] == 1 && imageIn[offset-1] == white) ||
            (struc[4] == 1 && imageIn[offset] == white) ||
            (struc[5] == 1 && imageIn[offset+1] == white) ||
            (struc[6] == 1 && imageIn[offset-1+width] == white) ||
            (struc[7] == 1 && imageIn[offset+width] == white) ||
            (struc[8] == 1 && imageIn[offset+1+width] == white))
                 imageOut[offset] = white;
         else
                 imageOut[offset] = black;
	}
    }
}

void IPL_dilation(BYTE * imageIn, BYTE * imageOut, char *struc)
{
    int x, y;
    int offset;

    for (y = 1; y < height - 1; y++)
    {
        for (x = 1; x < width - 1; x++)
	{
            offset = x + y * width;

        if ((struc[0] == 1 && imageIn[offset-1 - width] == black) ||
            (struc[1] == 1 && imageIn[offset   - width] == black) ||
            (struc[2] == 1 && imageIn[offset+1 - width] == black) ||
            (struc[3] == 1 && imageIn[offset-1] == black) ||
            (struc[4] == 1 && imageIn[offset] == black) ||
            (struc[5] == 1 && imageIn[offset+1] == black) ||
            (struc[6] == 1 && imageIn[offset-1+width] == black) ||
            (struc[7] == 1 && imageIn[offset+width] == black) ||
            (struc[8] == 1 && imageIn[offset+1+width] == black))
                imageOut[offset] = black;
        else
                imageOut[offset] = white;
	}
    }
}

void IPL_open(BYTE * imageIn, BYTE * imageOut, char *struc)
{
    IPL_erosion(imageIn, &h1[0], struc);
    IPL_dilation(&h1[0], imageOut, struc);
}

void IPL_close(BYTE * imageIn, BYTE * imageOut, char *struc)
{
    IPL_dilation(imageIn, &h1[0], struc);
    IPL_erosion(&h1[0], imageOut, struc);
}


void IPL_overlay(BYTE *imageIn1, BYTE *imageIn2, BYTE *imageOut, BYTE gray_val)
/* copy imageIn1 to image Out i                                         */
/* change all positions in which imageIn2 = black to gray_val in output */
{
    int i;
    memcpy(imageOut, imageIn1, width * height);
    for (i = 0; i < width * height; i++)
      if (imageIn2[i] == black) imageOut[i] = gray_val;

}

void IPL_Coverlay(BYTE *imageIn1, BYTE *imageIn2, BYTE *imageOut, BYTE r, BYTE g, BYTE b)
/* copy imageIn1 to image Out i                                         */
/* change all positions in which imageIn2 = black to RGB_val in output  */
/* ImageIn1 -> COL, ImageIn2 -> GRAY, ImageOut -> COL                   */
{
    int i;
    memcpy(imageOut, imageIn1, 3*width * height);
    for (i = 0; i < width * height; i++)
      if (imageIn2[i] == black)
        {  imageOut[3*i  ] = r;
           imageOut[3*i+1] = g;
           imageOut[3*i+2] = b;
        }
}


/* 2x2 ordered dithering, uses only upper left pixel in 2x2 areas      */

void IPL_dither(BYTE *imageIn, BYTE *imageOut)
{
    int x, y, i;
    int thres = white / 5;
    memcpy(imageOut, imageIn, width * height);

    for (y = 0; y < height-1; y+=2)
    {
        for (x = 0; x < width-1; x+=2)
        {
            i = x + width * y;

            if (imageIn[i] <   thres)
                imageOut[i] = black;
            else
                imageOut[i] = white;

            if (imageIn[i] < 3*thres)
                imageOut[i+1] = black;
            else
                imageOut[i] = white;

            if (imageIn[i] < 4*thres)
                imageOut[i+width] = black;
            else
                imageOut[i] = white;

            if (imageIn[i] < 2*thres)
                imageOut[i+width+1] = black;
            else
                imageOut[i] = white;
        }
    }
}


void col_invert(BYTE *rgb)
{ /* invert 3 consecutive RGB bytes */
    *rgb     = 255 - *rgb;
    *(rgb+1) = 255 - *(rgb+1);
    *(rgb+2) = 255 - *(rgb+2);
}

void IPL_showxy(BYTE *imageIn, BYTE *imageOut, int x, int y)
{   
    memcpy(imageOut, imageIn, 3 * width * height);
    printf("show (%d,%d) = %d", x, y, imageIn[x + y * width]);

    if (x > 1 && x < width - 1 && y > 1 && y < height - 1)
    {
     imageOut[x + y * width    ] = white - imageIn[x + y * width    ];
     imageOut[x + y * width + 1] = white - imageIn[x + y * width + 1];
     imageOut[x + y * width + 2] = white - imageIn[x + y * width + 2];
     imageOut[x + y * width - 1] = white - imageIn[x + y * width - 1];
     imageOut[x + y * width - 2] = white - imageIn[x + y * width - 2];
     imageOut[x + (y+1) * width] = white - imageIn[x + (y+1) * width];
     imageOut[x + (y+2) * width] = white - imageIn[x + (y+2) * width];
     imageOut[x + (y-1) * width] = white - imageIn[x + (y-1) * width];
     imageOut[x + (y-2) * width] = white - imageIn[x + (y-2) * width];
    }
}

void IPL_Cshowxy(BYTE *imageIn, BYTE *imageOut, int x, int y)
{   int pos, pos2;

    memcpy(imageOut, imageIn, 3 * width * height);

    pos = 3*(x + y * width);
    printf("show (%d,%d) = %d %d %d", x, y,
                 imageIn[pos], imageIn[pos+1], imageIn[pos+2]);

    if (x > 1 && x < width - 1 && y > 1 && y < height - 1)
    {
     col_invert(&imageOut[pos]);
     col_invert(&imageOut[pos+3]);
     col_invert(&imageOut[pos-3]);
     col_invert(&imageOut[pos+6]);
     col_invert(&imageOut[pos-6]);
     pos2 = pos + 3 * width;
     col_invert(&imageOut[pos2]);
     pos2 = pos + 6 * width;
     col_invert(&imageOut[pos2]);
     pos2 = pos - 3 * width;
     col_invert(&imageOut[pos2]);
     pos2 = pos - 6 * width;
     col_invert(&imageOut[pos2]);
    }
}


void IPL_fill (BYTE *imageIn, BYTE *imageOut, int x, int y, char * struc)
{
    BYTE grow[MAX_IMAGE_SIZE];
    BYTE new_grow[MAX_IMAGE_SIZE];
    BYTE not_img[MAX_IMAGE_SIZE];
    int loopCount = 0;

    IPL_negation(imageIn, &not_img[0]);
    memset(&new_grow[0], white, width * height);
    new_grow[x + y * width] = black;

    do
    {
        memcpy(&grow[0], &new_grow[0], width * height);
        IPL_dilation(&grow[0], &new_grow[0], struc);
	IPL_or(&new_grow[0], &not_img[0], &new_grow[0]);

        loopCount++;
    }
    while (!IPL_equal(&grow[0], &new_grow[0]));

    memcpy(imageOut, &grow[0], width * height);

    printf("fill loops = %d", loopCount);
}

void IPL_region_growing(BYTE *imageIn, BYTE *imageOut, int thresh)
{
    /* thresh = 10..100 */

    int seed = 40;
    int change;
    int x, y, offset = 0, off2 = 1;
    int loopCount = 0;
    BYTE reg[MAX_IMAGE_SIZE];

    memset(reg, 0, width * height);

    for (y = 0; y < height; y++)
        for (x = 0; x < width; x++, offset++)
            if ((x % seed == 1) && (y % seed == 1))
                reg[offset] = off2++;

    do
    {
        change = 0;

        for (y = 1; y < height-1; y++)
	{
            for (x = 1; x < width-1; x++)
	    {
                offset = x + width * y;

                if (reg[offset] != 0)
		{
                    if (abs(imageIn[offset] - imageIn[offset - 1]) <= thresh &&
                    reg[offset - 1] < reg[offset])
                        change = reg[offset - 1] = reg[offset];

                if (abs(imageIn[offset] - imageIn[offset + 1]) <= thresh &&
                    reg[offset + 1] < reg[offset])
                        change = reg[offset + 1] = reg[offset];

                if (abs(imageIn[offset] - imageIn[offset - width]) <= thresh &&
                    reg[offset - width] < reg[offset])
                        change = reg[offset - width] = reg[offset];

                if (abs(imageIn[offset] - imageIn[offset + width]) <= thresh &&
                    reg[offset + width] < reg[offset])
                        change = reg[offset + width] = reg[offset];
		  }
	    }
	}

        loopCount++;
    }
    while (change);

    memcpy(imageOut, reg, width * height);

    printf("reg(s=%d,t=%d) l=%d", seed, thresh, loopCount);
}

int IPL_equal_nobound(BYTE *image1, BYTE *image2)
{
    int x, y;

    for (y = 1; y < height - 1; y++)
        for (x = 1; x < width - 1; x++)
            if (image1[x + y * width] != image2[x + y * width])
                return 0;

    return 1;
}


int IPL_equal(BYTE *imageIn1, BYTE *imageIn2)
{
    int i;

    for (i = 0; i < width * height; i++)
        if (imageIn1[i] != imageIn2[i])
            return 0;

    return 1;
}


void IPL_and(BYTE *imageIn1, BYTE * imageIn2, BYTE *imageOut)
{
    int i;

    for (i = 0; i < width * height; i++)
        imageOut[i] = imageIn1[i] & imageIn2[i];
}

void IPL_or(BYTE *imageIn1, BYTE * imageIn2, BYTE *imageOut)
{
    int i;

    for (i = 0; i < width * height; i++)
        imageOut[i] = imageIn1[i] | imageIn2[i];
}


void IPL_connected (BYTE *imageIn, BYTE *imageOut, int x, int y, char *struc)
{
    BYTE grow[MAX_IMAGE_SIZE];
    BYTE new_grow[MAX_IMAGE_SIZE];
    int loopCount = 0;

    memset(&new_grow[0], white, width * height);
    new_grow[x + y * width] = black;

    do
    {
        memcpy(&grow[0], &new_grow[0], width * height);
        IPL_dilation(&grow[0], &new_grow[0], struc);
	IPL_or(&new_grow[0], imageIn, &new_grow[0]);

        loopCount++;
    }
    while (!IPL_equal(&grow[0], &new_grow[0]));

    memcpy(imageOut, &grow[0], width * height);

    printf("connected loops = %d", loopCount);
}

void IPL_boundary (BYTE *imageIn, BYTE *imageOut, char *struc)
{
    IPL_erosion(imageIn, imageOut, struc);
    IPL_negation(imageOut, imageOut);
    IPL_or(imageIn, imageOut, imageOut);
}

void IPL_skeleton (BYTE *imageIn, BYTE *imageOut, char * struc)
{
    BYTE help[MAX_IMAGE_SIZE];
    BYTE k_times[MAX_IMAGE_SIZE];
    int loopCount = 0;

    memset(imageOut, white, width * height);
    memcpy(&k_times[0], imageIn, width * height);

    while(IPL_count_nobound(&k_times[0], &help[0], black) > 0)
    {
        IPL_open(&k_times[0], &help[0], struc);
        IPL_negation(&help[0], &help[0]);
        IPL_or(&help[0], &k_times[0], &help[0]);
        IPL_and(&help[0], imageOut, imageOut);

        memcpy(&help[0], &k_times[0], width * height);
        IPL_erosion(&help[0], &k_times[0], struc);

        loopCount++;
    }

    printf("skeleton loops = %d", loopCount);
}

int IPL_count(BYTE *imageIn, BYTE *imageOut, BYTE val)
{
    int i;
    int count;

    memcpy(imageOut, imageIn, width * height); /* identity */

    for (i = 0, count = 0; i < height * width; i++)
        if (imageIn[i] == val) count++;

    return count;
}

int IPL_count_nobound(BYTE *imageIn, BYTE *imageOut, BYTE val)
{
    int x, y;
    int count = 0;

    memcpy(imageOut, imageIn, width * height); /* identity */

    for (y = 1; y < height - 1; y++)
        for (x = 1; x < width - 1; x++)
            if (imageIn[x + y * width] == val) count++;

    return count;
}


void IPL_corner(BYTE *imageIn, BYTE *imageOut)
{
    int i;
    int b;

    for (i = width; i < (height - 1) * width; i++)
    {
        b = abs(-imageIn[i-width-1] - imageIn[i-width] - imageIn[i-width+1] -
                 imageIn[i-1]       + 8 * imageIn[i]   - imageIn[i+1] -
                 imageIn[i+width-1] - imageIn[i+width] - imageIn[i+width+1])/9;

        if (b > white)
            imageOut[i] = white;
        else
            imageOut[i] = (BYTE)b;
    }
}



/*
 *      contour search with chain code
 *
 */

static int contourX[] = { 1,  1,  0, -1, -1, -1, 0, 1 };
static int contourY[] = { 0, -1, -1, -1,  0,  1, 1, 1 };
static IPLPoint contour[MAX_CONTOUR];
static double dcoord[MAX_CONTOUR];


static int IsEdge(BYTE * image, int x, int y, int w, BYTE threshold)
{
    int i;
    int left = 0, right = 0;
    BYTE * p;

    if (x - w < 0 || x + w >= width)
        return 0;

    p = &image[y * width + x - w];

    for (i = 0; i < w; i++)
    {
        left  += p[i];
        right += p[i + w];
    }

    if (right - left > threshold * w)
        return 1;
    else
        return 0;
}

int IPL_FindCircles(BYTE * imageIn, BYTE * imageOut, XYDistance * result)
{
    int i;
    int count;
    IPLPoint points[256];

    count = IPL_contour(imageIn, imageOut, &points[0], 8);
    if (result == NULL)
        return count;

    for (i = 0; i < count; i++)
    {
        result[i].x = (double)points[i].x / width;
        result[i].y = (double)(height - points[i].y) / height;
    }

    return count;
}


int  IPL_contour(BYTE * imageIn,
                 BYTE * imageOut,
		 IPLPoint * result,
                 BYTE threshold)
{
    int x, y;
    int countCircles = 0;

    if (imageOut != NULL)
        memset(imageOut, 0, width * height);

    for (y = 1; y < height - 1; y++)
    {
        for (x = 1; x < width - 2; x++)
	{
            if (imageIn[x + y * width] != markerDone &&
                IsEdge(imageIn, x, y, 2, 10))
	    {
                unsigned length = FollowContour(imageIn,
                                                x,
                                                y,
                                                threshold,
                                                &contour[0],
                                                MAX_CONTOUR);


                if (length > 4 && length < 30)
		{
                    IPLPoint p;

                    if (CheckCircle(length, &contour[0], &p) == 0)
		    {
                        if (result != NULL)
                            result[countCircles] = p;

                        if (imageOut != NULL)
			{
                            imageOut[p.x + p.y * width] = markerContour;

                            while (length-- > 0)
                            {
                                imageOut[contour[length].x + 
                                         contour[length].y * width] = markerContour;
			    }
                        }

                        countCircles++;
                    }
                }
	    }
	}
    }

    printf("%d circles found\n", countCircles);

    return countCircles;
}

static int FollowContour(BYTE * imageIn,
                         int x,
                         int y,
                         BYTE threshold,
                         IPLPoint * points,
                         int maxLength)
{
    int done = 0;
    int startX = x;
    int startY = y;
    int newX, newY;
    unsigned int direction = 6;
    unsigned int newDir;
    unsigned int length = 0;

    /*
     *    follow the contour as long as the 'done' flag is not set
     */

    while (!done)
    {
        int i;

        imageIn[x + y * width] = markerDone;

        /*
         *    store the coordinates in the 'points' array if a valid
         *    pointer is supplied and 'maxLength' is not reached.
         */

        if (points != NULL && length < maxLength)
        {
            points[length].x = (short)x;
            points[length].y = (short)y;
	}

        length++;

        for (i = 0; i < 8; i++)
        {
            newDir = (direction + i - 3) % 8;
            newX = x + contourX[newDir];
            newY = y + contourY[newDir];

            if (newX >= 0 && newY >= 0 && newX < width && newY < height &&
                imageIn[newX + newY * width] > threshold)
                    break;
	}

        if (i >= 8)
            done = 1;
        else
        {
            x = newX;
            y = newY;
            direction = newDir;
        }

        if (x == startX && y == startY)
            done = 1;
    }

    return length;
}

static int CheckCircle(int length, IPLPoint * contour, IPLPoint * cog)
{
    int i;
    double cogX = contour[0].x;
    double cogY = contour[0].y;
    double cogDelta = 0.0;
    double cogVar = 0.0;

    if (length <= 0)
        return -1;

    /*
     *    Determine the center of gravity. Take the x/y ratio of 4/3
     *    into account.
     */

    for (i = 1; i < length; i++)
    {
        cogX += contour[i].x;
        cogY += contour[i].y;
    }

    cogY = 4.0 * cogY / (3.0 * length);
    cogX /= length;

    cog -> x = (short)cogX;
    cog -> y = (short)(cogY * 0.75);

    /*
     *
     */

    for (i = 0; i < length; i++)
    {
        double delta =  sqrt((contour[i].x - cogX) * (contour[i].x - cogX) +
                             (4.0 * contour[i].y / 3.0 - cogY) *
                             (4.0 * contour[i].y / 3.0 - cogY));
        dcoord[i] = delta;
        cogDelta += delta;
    }

    cogDelta /= length;

    for (i = 0; i < length; i++)
        cogVar += (cogDelta - dcoord[i]) * (cogDelta - dcoord[i]);
    cogVar /= length;


    if (cogVar < 0.45)
        return 0;
    else
        return 1;
}

