1 module dcv.example.convolution;
2 
3 /** 
4  * Spatial image filtering example using dcv library.
5  */
6 
7 import std.stdio : writeln;
8 import std.datetime : StopWatch;
9 import std.math : abs;
10 import std.array : array;
11 
12 import mir.ndslice;
13 
14 import dcv.core : Image, ranged, ImageFormat;
15 import dcv.io : imread, imwrite;
16 import dcv.imgproc;
17 import dcv.plot;
18 
19 int main(string[] args)
20 {
21     string impath = (args.length < 2) ? "../data/lena.png" : args[1];
22 
23     Image img = imread(impath); // read an image from filesystem.
24 
25     if (img.empty)
26     { // check if image is properly read.
27         writeln("Cannot read image at: " ~ impath);
28         return 1;
29     }
30 
31     Slice!(Contiguous, [3], float*) imslice = img
32         .sliced // slice image data
33         .as!float // convert it to float
34         .slice; // make a copy.
35 
36     auto gray = imslice.rgb2gray; // convert rgb image to grayscale
37 
38     auto gaussianKernel = gaussian!float(2, 5, 5); // create gaussian convolution kernel (sigma, kernel width and height)
39     auto sobelXKernel = sobel!real(GradientDirection.DIR_X); // sobel operator for horizontal (X) gradients
40     auto laplacianKernel = laplacian!double; // laplacian kernel, similar to matlabs fspecial('laplacian', alpha)
41     auto logKernel = laplacianOfGaussian!float(1, 5, 5); // laplacian of gaussian, similar to matlabs fspecial('log', alpha, width, height)
42 
43     // perform convolution for each kernel
44     auto blur = imslice.conv(gaussianKernel);
45     auto xgrads = gray.conv(sobelXKernel);
46     auto laplaceEdges = gray.conv(laplacianKernel);
47     auto logEdges = gray.conv(logKernel);
48 
49     // calculate canny edges
50     auto cannyEdges = gray.canny!ubyte(75);
51 
52     // perform bilateral blurring
53     auto bilBlur = imslice.bilateralFilter!float(10.0f, 10.0f, 5);
54 
55     // Add salt and pepper noise at input image green channel
56     auto noisyImage = imslice.slice;
57     auto saltNPepperNoise = noisyImage[0 .. $, 0 .. $, 1].saltNPepper(0.15f);
58     // ... and perform median blurring on noisy image
59     auto medBlur = noisyImage.medianFilter(5);
60 
61     // scale values from 0 to 255 to preview gradient direction and magnitude
62     xgrads.ranged(0, 255);
63     // Take absolute values and range them from 0 to 255, to preview edges
64     laplaceEdges = laplaceEdges.map!(a => abs(a)).slice.ranged(0.0f, 255.0f);
65     logEdges = logEdges.map!(a => abs(a)).slice.ranged(0.0f, 255.0f);
66 
67     // Show images on screen
68     img.imshow("Original");
69     bilBlur.imshow("Bilateral Blurring");
70     noisyImage.imshow("Salt and Pepper noise at green channel for Median");
71     medBlur.imshow("Median Blurring");
72     blur.imshow("Gaussian Blurring");
73     xgrads.imshow("Sobel X");
74     laplaceEdges.imshow("Laplace");
75     logEdges.imshow("Laplacian of Gaussian");
76     cannyEdges.imshow("Canny Edges");
77 
78     waitKey();
79 
80     return 0;
81 }
82 
83 auto saltNPepper(T, SliceKind kind)(Slice!(kind, [2], T*) input, float saturation)
84 {
85     import std.range : lockstep;
86     import std.random : uniform;
87 
88     int err;
89     ulong pixelCount = input.length!0*input.length!1;
90     ulong noisyPixelCount = cast(typeof(pixelCount))(pixelCount * saturation);
91 
92     auto noisyPixels = slice!size_t(noisyPixelCount);
93     foreach(ref e; noisyPixels)
94     {
95         e = uniform(0, pixelCount);
96     }
97 
98     auto imdata = input.reshape([pixelCount], err);
99 
100     assert(err == 0);
101 
102     foreach(salt, pepper; lockstep(noisyPixels[0 .. $ / 2], noisyPixels[$ / 2 .. $]))
103     {
104         imdata[salt] = cast(T)255;
105         imdata[pepper] = cast(T)0;
106     }
107     return input;
108 }