1 module dcv.example.rht;
2 
3 /** 
4  * Randomized Hough Transform example using dcv library.
5  */
6 
7 import std.stdio : writeln;
8 import std.datetime : StopWatch;
9 import std.math : fabs, PI, sin, cos, rint;
10 import std.typecons : tuple;
11 
12 import mir.ndslice;
13 
14 import dcv.core.image : Image, ImageFormat;
15 import dcv.core.utils : clip;
16 import dcv.io : imread, imwrite;
17 import dcv.imgproc;
18 import dcv.features.rht;
19 
20 void plotLine(T, Line, Color)(Slice!(Contiguous, [3], T*) img, Line line, Color color)
21 {
22     int height = cast(int)img.length!0;
23     int width = cast(int)img.length!1;
24     if (line.m == double.infinity)
25     {
26         auto x = line.b;
27         if (x >= 0 && x < width)
28             foreach (y; 0 .. height)
29             {
30                 img[cast(int)y, cast(int)x, 0 .. 3] = color;
31             }
32     }
33     else
34     {
35         foreach (x; 0 .. 1000)
36         {
37             auto y = line.m * x + line.b;
38             if (x >= 0 && x < width && y >= 0 && y < height)
39             {
40                 img[cast(int)y, cast(int)x, 0 .. 3] = color;
41             }
42         }
43     }
44 }
45 
46 void plotCircle(T, Circle, Color)(Slice!(Contiguous, [3], T*) img, Circle circle, Color color)
47 {
48     int height = cast(int)img.length!0;
49     int width = cast(int)img.length!1;
50     // quick and dirty circle plot
51     foreach (t; 0 .. 360)
52     {
53         int x = cast(int)rint(circle.x + circle.r * cos(t * PI / 180));
54         int y = cast(int)rint(circle.y + circle.r * sin(t * PI / 180));
55         if (x >= 0 && x < width && y >= 0 && y < height)
56             img[y, x, 0 .. 3] = color;
57     }
58 }
59 
60 int main(string[] args)
61 {
62 
63     string impath = (args.length < 2) ? "../data/img.png" : args[1];
64 
65     Image img = imread(impath); // read an image from filesystem.
66 
67     if (img.empty)
68     { // check if image is properly read.
69         writeln("Cannot read image at: " ~ impath);
70         return 1;
71     }
72 
73     Slice!(Contiguous, [3], float*) imslice = img.sliced.as!float.slice; // convert Image data type from ubyte to float
74 
75     auto gray = imslice.rgb2gray; // convert rgb image to grayscale
76 
77     auto gaussianKernel = gaussian!float(2, 3, 3); // create gaussian convolution kernel (sigma, kernel width and height)
78 
79     auto blur = gray.conv(gaussianKernel);
80     auto canny = blur.canny!ubyte(150);
81 
82     auto lines = RhtLines().epouchs(35).iterations(500).minCurve(70);
83     StopWatch s;
84     s.start;
85     auto linesRange = lines(canny);
86     foreach (line; linesRange)
87     {
88         writeln(line);
89         plotLine(imslice, line, [1.0, 1.0, 1.0]);
90     }
91     s.stop;
92     writeln("RHT lines took ", s.peek.msecs, "ms");
93     writeln("Points left after lines:", linesRange.points.length);
94     auto circles = RhtCircles().epouchs(15).iterations(2000).minCurve(50);
95     s.reset;
96     s.start;
97     foreach (circle; circles(canny, linesRange.points[]))
98     {
99         writeln(circle);
100         plotCircle(imslice, circle, [1.0, 1.0, 1.0]);
101     }
102     s.stop;
103     writeln("RHT circles took ", s.peek.msecs, "ms");
104 
105     // write resulting images on the filesystem.
106     blur.map!(v => v.clip!ubyte).slice.imwrite(ImageFormat.IF_RGB, "./result/outblur.png");
107     canny.map!(v => v.clip!ubyte).slice.imwrite(ImageFormat.IF_MONO, "./result/canny.png");
108     imslice.map!(v => v.clip!ubyte).slice.imwrite(ImageFormat.IF_RGB, "./result/rht.png");
109 
110     return 0;
111 }