#include "image_classes.h" #include // This function computes the hough transform and populates // the accumulator array void HoughLine::ComputeTransform() { int max_radius; int t, r, x, y, i; unsigned char *edge_image; EdgeDetector ef; // max_radius is set to the diagonal length of the image // and max_theta is set to 360 degrees max_radius = (int) sqrt(width*width + height*height); h_width = 360; h_height = (int)max_radius; // Allocate memory for a 2D accumulator array accumulator = (unsigned char **)malloc(h_height * sizeof(unsigned char *)); for (i = 0; i < h_height; i++) accumulator[i] = (unsigned char *) calloc(h_width, sizeof(unsigned char)); // Do edge detection using the Sobel operator // Calls the EdgeDetector class ef.SetImage(in_image, width, height); if (use_direction) edge_image = ef.FindEdgesUsingGradientDirection(edge_threshold, edge_direction); else edge_image = ef.FindEdgesUsingGradient(edge_threshold); // Loop through all pixels and for each edge pixel, // plot the corresponding curve in the Hough space. for (i = 0; i < numpixels; i++) { if (edge_image[i] > 0) // This is an edge pixel { x = i%width; y = i/width; // Draw the curve in the Hough space for (t = 0; t < 360; t++) { r = (int)((x - width/2)*COS[t] + (y - height/2)*SIN[t]); if ((r > -max_radius/2) && (r < max_radius/2) && (r != 0)) ++accumulator[r+max_radius/2][t]; } } } free(edge_image); // we do not need the edge image any more } // This function detects lines in a given image // by looking for maximas in the accumulator array void HoughLine::DetectLines() { int max_accum = 0; int r, t, dr, dt; int maxima, max_value = 0; if (accumulator == NULL) ComputeTransform(); // Find the maximum accumulator value. We use this // later to consider only the top 30% of the values // when looking for local maximas for (r = 0; r < h_height; r++) for (t = 0; t < h_width; t++) if (accumulator[r][t] > max_value) max_value = accumulator[r][t]; // Scan the accumulator array for local maximas // If one is found, draw the corresponding line in the // original image for (r = 4; r < h_height - 5; r++) for (t = 4; t < h_width - 5; t++) { if (accumulator[r][t] > 0.7 * max_value) { maxima = TRUE; // Check a 9x9 window for (dr = r-4; dr < r+5; dr++) for (dt = t-5; dt < t+5; dt++) if (accumulator[dr][dt] > accumulator[r][t]) maxima = FALSE; if (maxima == TRUE) { DrawLine((float)(r - h_height/2), (float)((t-180)*pi/180.0)); } } } } ////////////////////////////////////////////////////////// // This function draws a line in an image for a // // given theta and radius. Modified to accomodate my // // image conventions // // // // Author: John Gauch // // University of Kansas // ////////////////////////////////////////////////////////// void HoughLine::DrawLine(float radius, float theta) { float M, B; int x, y; int start, end; // Handle lines with X changing faster than y */ if (((theta >= pi/4) && (theta <= 3*pi/4)) || ((-theta >= pi/4) && (-theta <= 3*pi/4))) { M = (float) cos(theta) / (float) sin(theta); B = radius / (float) sin(theta) + (width/2)*M + height/2; start = end = -1; for (x = 0; x < width; x++) { y = (int) (0.5 + B - x * M); if ((y >= 0) && (y < height)) if (start < 0) start = x; else { end = x; for (x = start; x < end; x++) { y = (int) (0.5 + B - x * M); edge_image[y*width + x] = 255; } start = end = -1; } } } else { M = (float) sin(theta) / (float) cos(theta); B = radius / (float) cos(theta) + (height/2) * M + width/2; start = end = -1; for (y = 0; y < height; y++) { x = (int) (0.5 + B - y * M); if ((x >= 0) && (x < width)) if (start < 0) start = y; else { end = y; for (y = start; y < end; y++) { x = (int) (0.5 + B - y * M); edge_image[y*width + x] = 255; } start = end = -1; } } } } // Set the image and its dimensions void HoughLine::SetImage(unsigned char *image, int w, int h) { width = w; height = h; numpixels = width * height; in_image = (unsigned char *) malloc(numpixels); memcpy(in_image, image, numpixels); edge_image = (unsigned char *) calloc(numpixels, 1); } // This function returns the binary edge image unsigned char *HoughLine::GetEdgeImage() { unsigned char *out_image; out_image = (unsigned char *)malloc(numpixels); memcpy(out_image, edge_image, numpixels); return out_image; } // This function returns edge lines superimposed on // the original image unsigned char *HoughLine::GetSuperimposedImage() { int i; unsigned char *out_image; out_image = (unsigned char *)malloc(numpixels); memcpy(out_image, in_image, numpixels); // Mark all edge pixels for (i = 0; i < numpixels; i++) if (edge_image[i] > 0) out_image[i] = 255; return out_image; } // This function converts the 2D accumulator array // to a 1D image array so that it can be displayed on // the screen unsigned char * HoughLine::GetHoughImage(int *hough_width, int *hough_height) { unsigned char *hough_image; int row, col; if (accumulator == NULL) ComputeTransform(); // allocate memory for the image hough_image = (unsigned char *)malloc(h_width * h_height); *hough_width = h_width; *hough_height = h_height; // transfer values from the accumulator to the pixel array for (row = 0; row < h_height; row++) for (col = 0; col < h_width; col++) hough_image[row*h_width + col] = accumulator[row][col]; return hough_image; } // The constructor. Initializes the SIN and COS tables HoughLine::HoughLine() { double theta; pi = 3.141592654; in_image = NULL; edge_image = NULL; accumulator = NULL; edge_threshold = 12; edge_direction = 10; use_direction = 0; // Allocate memory for SIN and COS lookup tables SIN = (float *) malloc(360 * sizeof(float)); COS = (float *) malloc(360 * sizeof(float)); // Fill in the SIN and COS lookup tables for (int t = 0; t < 360; t++) { theta = (t - 180) * pi / 180; SIN[t] = (float) sin(theta); COS[t] = (float) cos(theta); } } // The destructor. Frees all allocated memory HoughLine::~HoughLine() { int row; if (in_image != NULL) free (in_image); if (edge_image != NULL) free (edge_image); if (accumulator != NULL) for (row = 0; row < h_height; row++) free (accumulator[row]); free(accumulator); free (SIN); free (COS); }