/* 
 * hough.c
 * 
 * Practical Algorithms for Image Analysis
 * 
 * Copyright (c) 1997, 1998, 1999 MLMSoftwareGroup, LLC
 */

/* HOUGH:       program performs Hough transform on binary image
 *                    and yields an image of the transform space as well
 *                      as the line corresponding to the highest peak
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <tiffimage.h>          /* tiff info on images */
#include <images.h>
#include <math.h>
extern void print_sos_lic ();

#define ON 0
#define DFLT_BORDER 2           /* default border of image */
#if defined(WIN32)
#define PI 3.1415926536
#endif
#define PID2 1.5707963268
#define TAN_TOO_BIG 263.0       /* tan so big that angle is PI/2 */
#define INBOUNDX(X) ((X) >= 0 && (X) < width)
#define INBOUNDY(Y) ((Y) >= 0 && (Y) < height)

int usage (short);
int input (int, char **, long *, short *);

main (argc, argv)
     int argc;
     char *argv[];
{
  Image *imgI, *imgH;           /* I/O image structures */
  unsigned char **imgIn, **hough;  /* input/output images */
  long width, height;           /* input image size */
  long border;                  /* zero out border of image */
  short dFlag;                  /* if = 0, display peak in Hough space */
  long xEnd, yEnd;              /* end of image within borders */
  long thetaHt, rhoWid;         /* width and height of Hough space */
  long rhoWidM1;                /* rho width minus 1 */
  double rho, theta;            /* radius and angle in Hough space */
  double tanTheta;              /* tan of theta angle */
  double denom;                 /* denominator */
  double rhoNorm;               /* normalization factor for rho */
  long max, xMax, yMax;         /* peak point in Hough space */
  double x1, y1;
  long i, j;
  long x, y;

  if ((input (argc, argv, &border, &dFlag)) < 0)
    return (-1);

/* open input and output images */
  imgI = ImageIn (argv[1]);
  imgIn = imgI->img;
  height = ImageGetHeight (imgI);
  width = ImageGetWidth (imgI);
  printf ("image size is %dx%d.\n", width, height);

  rhoWid = width;
  thetaHt = height;
  rhoWidM1 = rhoWid - 1;
  imgH = ImageAlloc (thetaHt, rhoWid, 8);
  hough = ImageGetPtr (imgH);

/* initialize accumulator bins */
  for (i = 0; i < thetaHt; i++)
    for (j = 0; j < rhoWid; j++)
      hough[i][j] = 0;

/* perform Hough transform */

  rhoNorm = rhoWidM1 / sqrt ((double) (width * width) + (double) height * height);

  yEnd = height - border;
  xEnd = width - border;
  for (y = border + 1; y < yEnd; y++) {
    for (x = border + 1; x < xEnd; x++) {
      if (imgIn[y][x] == ON) {
        for (i = 0; i < thetaHt; i++) {
          theta = (double) i *PI / (double) thetaHt;
          tanTheta = tan (theta);
          if (tanTheta > TAN_TOO_BIG) {
            /*printf ("too big: x = %d, i = %d, j = %d\n", x, i, j);*/
            rho = (double) x;
          }
          else {
            denom = tanTheta * tanTheta + 1.0;
            y1 = ((double) y - (double) x * tanTheta) / denom;
            x1 = ((double) x * tanTheta * tanTheta - (double) y * tanTheta) / denom;
            rho = sqrt (x1 * x1 + y1 * y1);
          }
          j = (long) (rho * rhoNorm + 0.5);
          if (hough[i][j] < 255)
            hough[i][j]++;
        }
      }
    }
  }

/* determine peak in Hough space and (theta, rho) coords */
  max = 0;
  for (i = 0; i < thetaHt; i++) {
    for (j = 0; j < rhoWid; j++) {
      if (hough[i][j] > max) {
        max = hough[i][j];
        xMax = j;
        yMax = i;
      }
    }
  }
  if (max > 0) {
    rho = xMax / rhoNorm;
    theta = yMax * PI / (double) thetaHt;
    printf ("Line located with parameters:\n");
    printf ("\tpolar coords: (%5.2f, %5.2f) (rho [pixels], theta [radians])\n",
            rho, theta);
    if (dFlag) {                /* display peak if flag set */
      printf ("Peak located at: (%d,%d)\n", xMax, yMax);
      for (i = yMax - 3; i <= yMax + 3; i++) {
        if (INBOUNDY (i)) {
          if (INBOUNDX (xMax - 3))
            hough[i][xMax - 3] = (hough[i][xMax - 3] > 128) ? 0 : 255;
          if (INBOUNDX (xMax + 4))
            hough[i][xMax + 3] = (hough[i][xMax + 3] > 128) ? 0 : 255;
        }
      }
      for (j = xMax - 3; j <= xMax + 3; j++) {
        if (INBOUNDX (j)) {
          if (INBOUNDY (yMax - 3))
            hough[yMax - 3][j] = (hough[yMax - 3][j] > 128) ? 0 : 255;
          if (INBOUNDY (yMax + 4))
            hough[yMax + 3][j] = (hough[yMax + 3][j] > 128) ? 0 : 255;
        }
      }
    }
  }
  else
    printf ("No line found.\n");

/* invert Hough image for display */
  for (i = 0; i < thetaHt; i++)
    for (j = 0; j < rhoWid; j++)
      hough[i][j] = 255 - hough[i][j];

  ImageOut (argv[2], imgH);

  return (0);
}



/* USAGE:       function gives instructions on usage of program
 *                    usage: usage (flag)
 *              When flag is 1, the long message is given, 0 gives short.
 */

int
usage (flag)
     short flag;                /* flag =1 for long message; =0 for short message */
{

/* print short usage message or long */
  printf ("USAGE: hough inimg outimg [-b BORDER] [-d] [-L]\n");
  if (flag == 0)
    return (-1);

  printf ("\nhough transforms binary image from\n");
  printf ("spatial domain ((x,y) coordinates) to polar coordinate\n");
  printf ("domain ((rho, theta) coordinates); peak in the polar\n");
  printf ("(\"Hough\")domain indicates a dominant line in the spatial domain;\n");
  printf ("NOTE: origin (0,0) of Hough transform\n");
  printf ("      image is in top-left corner;\n");
  printf ("      rho increases along horizontal axis to\n");
  printf ("      maximum rho equal to image diagonal length;\n");
  printf ("      theta increases downward from 0 radians to PI radians.\n\n");
  printf ("ARGUMENTS:\n");
  printf ("    inimg: input image filename (TIF)\n");
  printf ("   outimg: output image filename (TIF)\n\n");
  printf ("OPTIONS:\n");
  printf ("  -b BORDER: to remove noise at borders of image by omitting\n");
  printf ("             image for this number of rows/cols [default = %d].\n", DFLT_BORDER);
  printf ("         -d: display peak in Hough transform space.\n");
  printf ("         -L: print Software License for this module\n");

  return (-1);
}


/* INPUT:       function reads input parameters
 *                  usage: input (argc, argv, &dFlag)
 */

#define USAGE_EXIT(VALUE) {usage (VALUE); return (-1);}

int
input (argc, argv, border, dFlag)
     int argc;
     char *argv[];
     long *border;              /* zero out border of image */
     short *dFlag;              /* if = 1, display peak */
{
  long n;

  if (argc == 1)
    USAGE_EXIT (1);
  if (argc == 2)
    USAGE_EXIT (0);

  *dFlag = 0;
  *border = DFLT_BORDER;

  for (n = 3; n < argc; n++) {
    if (strcmp (argv[n], "-b") == 0) {
      if (++n == argc || argv[n][0] == '-')
        USAGE_EXIT (0);
      *border = atol (argv[n]);
    }
    else if (strcmp (argv[n], "-d") == 0)
      *dFlag = 1;
    else if (strcmp (argv[n], "-L") == 0) {
      print_sos_lic ();
      exit (0);
    }
    else
      USAGE_EXIT (0);
  }

  return (0);
}
