Subscribe Bookmark RSS Feed
Craige_Hales

Staff

Joined:

Mar 21, 2013

Hough Line Transform

Question on HoughLineTransform got me started on this.

The HoughLineTransform turns straight line segments in an input matrix into into points in an output matrix. The bright spots in the output matrix represent an angle and distance from center of a line. The output matrix represents the line containing the segment (the end point information is mostly lost).

In the JSL below, the input matrix is 100x100, a field of zeros with ones where the lines are. The output matrix dimension is determined by the nAngle and nRadius parameters; the columns represent the angle of the line and the rows are distance from center. The gen function is called for each change of the handles in the left-hand graph. Gen re-manufactures the mat matrix (100x100) and calls the transform. Gen then rebuilds a bitmap with the transform's output and updates the left-hand picture. Most of the code is just window dressing; the call to the transform is near the middle.

mx = [10 10 90 90 20 80]; // starter points
my = [10 90 10 90 10 90];
mat = J( 100, 100, 0 ); // input to hough line transform

New Window( "Hough Line Transform",
H List Box(
radio = Radio Box( {"1", "2", "3"}, gen() ), // sometimes you want to see just one line
Graph Box( // left side is just a GUI to position 3 lines
Frame Size( 400, 400 ),
Y Scale( -10, 110 ),
X Scale( -10, 110 ),
xaxis( Add Ref Line( 50, "Dashed", blue, "", 1 ), showmajorticks( 0 ), showminorticks( 0 ), showlabels( 0 ) ),
xname( "drag the square handles" ),
yaxis( Add Ref Line( 50, "Dashed", blue, "", 1 ), showmajorticks( 0 ), showminorticks( 0 ), showlabels( 0 ) ),
yname( "" ),
// when the 6 handles are moved, they capture their location AND generate results
Handle( mx[1], my[1], mx[1] = clamp( x ); my[1] = clamp( y ); gen(); );
Handle( mx[2], my[2], mx[2] = clamp( x ); my[2] = clamp( y ); gen(); );
Line( mx[1 :: 2], my[1 :: 2] ); // draw the lines in the GUI
text({(mx[1]+mx[2])/2,(my[1]+my[2])/2},char(mod(360+180*atan(my[1]-my[2],mx[1]-mx[2])/pi(),180),4));// label angle
If( radio << get > 1,
Handle( mx[3], my[3], mx[3] = clamp( x ); my[3] = clamp( y ); gen(); );
Handle( mx[4], my[4], mx[4] = clamp( x ); my[4] = clamp( y ); gen(); );
Line( mx[3 :: 4], my[3 :: 4] );
text({(mx[3]+mx[4])/2,(my[3]+my[4])/2},char(mod(360+180*atan(my[3]-my[4],mx[3]-mx[4])/pi(),180),4));// label angle
);
If( radio << get > 2,
Handle( mx[5], my[5], mx[5] = clamp( x ); my[5] = clamp( y ); gen(); );
Handle( mx[6], my[6], mx[6] = clamp( x ); my[6] = clamp( y ); gen(); );
Line( mx[5 :: 6], my[5 :: 6] );
text({(mx[5]+mx[6])/2,(my[5]+my[6])/2},char(mod(360+180*atan(my[5]-my[6],mx[5]-mx[6])/pi(),180),4));// label angle
);
),
// reserve a place to display results
H List Box(
V List Box(
imgholder = Border Box( Left( 5 ), Right( 5 ), top( 5 ), bottom( 5 ), Text Box( "drag handles" ) ),
H Center Box( anglebox = Text Box( "angle" ) )
),
V Center Box( Text Box( "Distance from black line to blue center", <<rotatetext( "left" ) ) )
)
),
hcenterbox(textbox("
The blue cross hair and the nearest point on the line (defined by the black line segment) form a distance.
When the blue cross hair is on the line defined by the black segment, the red cross hair is centered vertically (zero distance).
The black segment's slope forms the angle. The displayed angle is for the point identified by the red cross hair, which is on the brightest point.
",<<setwrap(700)))
);

// keep the handles in range so the drawline function is happy
clamp = Function( {t},
Max( 1, Min( 99, t ) )
);

//
// the three lines in the gui generate three lines in mat
// then the transform runs on mat and the result updates
//
gen = Function( {},
{xy, r, c},
mat[0, 0] = 0; // reset mat
drawline( mx[1 :: 2], my[1 :: 2] ); // add lines to mat
If( radio << get > 1,
drawline( mx[3 :: 4], my[3 :: 4] )
);
If( radio << get > 2,
drawline( mx[5 :: 6], my[5 :: 6] )
);
// run hough...
result = Hough Line Transform( mat, nangle( 200 ), nradius( 200 ) );// just to make result matrix square
// normalize...
result = result / Max( result );
result = result^.5; // boost brightness
ResultRed = result;
If( 1,//radio << get == 1,
xy = Loc Max( result ); // maximum bright point in transform
r = Floor( (xy - 1) / N Cols( result ) ) + 1;
c = xy - (r - 1) * N Cols( result );
anglebox << settext( "Black line angle "||Char( 180 * c / N Cols( result ), 4 ) || "°" );
ResultRed[r, 0] = .5;
ResultRed[0, c] = .5;
);
// update the results
img = New Image( "rgb", {resultRed, result, result} ); // gray scale bitmap from result
img << scale( 2 ); // bigger
(imgholder << child) << delete; // swap out old
imgholder << append( img ); // for new
);

// taken from scripting index example, a fast drawline in a matrix
drawline = Function( {mx, my},
{dx = mx[2] - mx[1], dy = my[2] - my[1], adx = Abs( dx ), ady = Abs( dy ), m, b},
If( adx > ady,
xx = Round( mx[1] ) :: Round( mx[2] );
m = dy / dx;
b = my[1] - m * mx[1];
yy = Round( xx * m + b );
,
yy = Round( my[1] ) :: Round( my[2] );
m = dx / dy;
b = mx[1] - m * my[1];
xx = Round( yy * m + b );
);
singleindex = (N Row( mat ) - yy) * N Col( mat ) + xx;
Try( mat[singleindex] = 1, 0 ); /* catch indexing errors and ignore the problems */
);

gen(); // do the first round

 The JSL creates a GUI to help understand the Hough Line TransformThe JSL creates a GUI to help understand the Hough Line Transform

 

 

Article Tags