Ñîâðåìåííûå èíôîìàöèîííûå òåõíîëîãèè/2. Âû÷èñëèòåëüíàÿ òåõíèêà è ïðîãðàììèðîâàíèå

 

Pavlo Bazilinskyy

Mikkeli University of Applied Sciences, Finland

Implementing Hough transformation with C language of programming

 

The Hough transformation is a technique, which is used to detect and isolate objects of a predefined shape within an input image. The classical realisation of the Hough transformation requires defining types of shapes to be processed on input. The most commonly used utilisation of this tool is detection of lines or circles in a give image [6]. The Hough transform as it is mostly known today was introduced by Richard Duda and Peter Hart in 1972, they called their technique a "generalized Hough transform" [7].

This article takes a case of finding straight lines in the given image into account. In order to find a line on the image a set of discrete points that come out from such filtering algorithm as edge detection must be plotted into a line segment. The equation for a line is  y = ax + b and it can be plotted for each pair of points (x, y) on the image. As stated by McAullife (Year not give), a task of the Hough transformation is to find find the slope parameter a and the intercept parameter b for each line and then output the line.  However, the slope approaches infinity as the line becomes more vertical. This issue can be resolved if a parametric equation is used:

x *cos(theta) + y*sin(theta) = rho

where, rho is the length of a normal from the origin to the line and theta is the orientation of the line with respect to the X axis.

After (rho; theta) values are found for each point in the image the Hough space can be drawn. On the received graph points with the biggest concentrations of plotted dots indicate lines in the image.


Figure 1. (a) a straight line in the original coordinates described in terms of the length of a normal from the origin to the line - r and orientation theta; (b) the Hough plane where points A, B, and C are transformed into three sinusoidal curves [7].

 


The Hough transformation can be relatively easily implemented using such common languages of programming as C, C++ or Java [4]. In the scope of this article working with this technique in C language of programming is described. The algorithm that can be used to implement the Hough transformation can be outlined:

1.     Populate an array with (rho; theta) values.

2.     Detect number of lines.

3.     Draw Hough space.

4.     Draw lines based on the Hough space.

 

Firstly, a function for performing detecting lines houghTransformation() can be created. Extensive research was done on usage of matrixes with CUDA to store theta/rho values, but better results were achieved in handling 1d arrays on GPU level [1, 2]. To achieve that the accumulator array should be utilized in which “votes” are given for each point in the image that is given on input. The following approach can be used to populate a 1-dimensional array with (rho; theta) values for each point in the inputted image: votes are put into an accumulator array, where index i is calculated: i = rho + (theta * NUM), where NUM is a predefined value, that is always larger than a maximum value for rho. Hence, rho = i % NUM and theta = i / NUM.

Then, drawLine() function can be written that draws a line for a given theta/rho value. To draw a line on the image, (x;y) coordinates can be calculated using this formula to find y for each x: y = (rho - x*cos(theta)) / sin(theta).

The next step is to draw the Hough space of dimensions [Max value of rho; 180], where 180 is the max value for theta.

Figure 2. Hough Space with 4 lines detected (brightness increased).

 

Aforementioned approach works fine for detecting one line object. However, a problem of detecting and drawing multiple lines can also considered. To detect multiple lines a radius around high numbers of votes in the accumulator array, which indicates a detected line, can be defined; then the array can be looped backwards, calling drawLine() function for each detected line.

This problems requires complex calculations and thus should be solved using parallel computing. The tool used for writing this article was CUDA library from NVIDIA Corporation. This library allows programmers to utilize C and C++ languages of programming to perform complex parallel calculations [1, 3]. All CUDA functions that are used in examples in this article have <<<width, height>>> dimensions, which are determined on a stage, when an input image is loaded. It allows running width * height (dimensions of the image) number of threads to be run simultaneously, which results in satisfying performance.

 

Figure 3. Detection of 2 lines using the Hough transformation.

 

A final output of the programme is a window with an original image and images, which show transformation that the input image undertakes to come to a final result, that is used in the Hough transformation function. Then, the original image with detected lines drawn on top of it is presented along with the Hough space.

 

Listings of functions used for the Hough transformation using C language of programming:

 

houghTransformation():

// ==============================================

// CUDA function that calculates rho and theta values for

// detecting lines using Hough transformation algorithm for given values of

// (x;y). It uses an equation rho = x*cos(theta) + y*sin(theta), where x and y are

// coordinates of a pixel on the image. Rho is the length of a normal from the

// origin to  the line, theta is the orientation of the line with respect to the X

// axis. Line detection needs to output results into a 2d space[rho][theta].

// Parameters:      unsigned char *in          - pointer to the array with transformed

//                                                         image

//                            int *out                 - pointer to the array for storing

//                                                         histogram values

//                            int n_pixels          - number of pixels multiplied by

//                                                         sizeOf(int)

// Returns:   nothing

// ==============================================

 

__global__ void houghTransformation(unsigned char *in, int *out, int n_pixels, int width, int height) {

int thread_id = (blockIdx.x * blockDim.x) + (threadIdx.x);

int thetaDeg;

float rho;

if (thread_id < n_pixels) {

           if (in[thread_id] > 0) {

                    //Checking the colour. If it is more than zero then it is not black.

                    int y = thread_id / width;

                    int x = thread_id - (width * y);

                    for (thetaDeg = 0; thetaDeg < 180; ++thetaDeg) {

                              //Iterate through theta

                    float thetaRad = dev_degrees_to_radians(thetaDeg); // degrees into radians conversion

                     rho = x * cosf(thetaRad) + y * sinf(thetaRad);

                    out[rho + (thetaDeg * NUM)]++;//Increment a value in an accumulator array

                    }

           }

}

}

 

drawLine():

// ==============================================

// Function that draws the Hough space using *space.

// Parameters:      int *histogram      - array with theta(Y), rho(X) values.

//                            float rho               - value of rho used.

//                            float theta            - value of theta used.

//                            unsigned char *lineImage     - array to hold generated

//                                                                            image.

//                            int width               - width of the image.

//                            int height              - height of the image.

// Returns:   nothing

// ==============================================

 

void drawLine(int *histogram, float rho, float theta, unsigned char *lineImage, int width, int height) {

int x, y;

for(x = 0; x < width; x++){

           y = (int) (rho - x * cosf(theta)) / sinf(theta);

           set_pixel(lineImage, x, y, 178, 34, 34, 255, width, height);

}

}

 

To test the programme images custom images were used along with real photos. These factors affecting results were found: dark lines are harder to detect; solid background is required for better results; if the line is not clear enough the line is detected more than once.

To perform testing different images were used as input and 7 different values for <<<width, height>>> were given, as can be seen from the graphs below along with a control test on CPU level.

 

Figure 4. Timing data for images with 1 pencil (left) and 4 pencils (right).

 

It is clear from testing performed that after certain point increasing number of threads does not affect performance with a linear dependancy. It is, therefore, important to calculate threshold for the optimum number of threads for the best performance and cost efficiency [5].

 

Literature:

 

1.      NVIDIA Corporation. CUDA C Programming Guide. Nvidia Corporation - 2011. Referred 25.1.2011. Available in www-format: <http://developer.nvidia.com/nvidia-gpu-computing-documentation>.

2.      NVIDIA Corporation. CUDA C Best Practices Guide. Nvidia Corporation - 2011. Referred 25.1.2011. Available in www-format: <URL: http://developer.nvidia.com/nvidia-gpu-computing-documentation>.

3.      NVIDIA Corporation. CUDA C Getting Started Guide for Linux. Nvidia Corporation - 2011. Referred 25.1.2011. Available in www-format: <URL: http://developer.nvidia.com/nvidia-gpu-computing-documentation>.

4.       Kim King. C Programming: A Modern Approach. 2nd ed. W. W. Norton & Company - 2008.

5.      Stephen Keckler, William Dally, Brucek Khailany, Michael Gerland, David Glasco. GPUs and the Future of Parallel Computing. Nvidia. Computing Now November, p. 1 - 2011

6.      Matthew McAullife. Hough Transform, Imaging Sciences Laboratory. Year not given. Referred 26.1.2011. Available in www-format: <URL: http://mipav.cit.nih.gov/documentation/HTML%20Algorithms/HoughTransform.html>

7.      Richard Duda and Peter Hart. Use of the Hough Transformation to Detect Lines and Curves in Pictures. Comm. ACM, Vol. 15, pp. 11–15 - January, 1972