1 /**
2 Image thresholding module.
3 
4 Copyright: Copyright Relja Ljubobratovic 2016.
5 
6 Authors: Relja Ljubobratovic
7 
8 License: $(LINK3 http://www.boost.org/LICENSE_1_0.txt, Boost Software License - Version 1.0).
9 */
10 module dcv.imgproc.threshold;
11 
12 import mir.ndslice;
13 import mir.ndslice.algorithm : each;
14 
15 import dcv.core.utils : emptySlice;
16 
17 /**
18 Clip slice values by a given threshold value.
19 
20 If any slice element has value in range between lower and 
21 upper threshold value, its output value is set to upper 
22 clipping value, otherwise to 0. If output value type is 
23 a floating point, upper clipping value is 1.0, otherwise 
24 its the maximal value for that type (e.g. for ubyte its 255, 
25 for float and double its 1.0).
26 
27 If lower threshold bound is of the same value as higher, then
28 values are clipped from given value to 0.
29 
30 Thresholding is supported for 1D, 2D and 3D slices.
31 
32 Params:
33     input = Input slice.
34     lowThresh   = Lower threshold value.
35     highThresh  = Higher threshold value.
36     prealloc    = Optional pre-allocated slice buffer for output.
37 
38 Note:
39     Input and pre-allocated buffer slice, should be of same structure
40     (i.e. have same strides). If prealloc buffer is not given, and is
41     allocated anew, input slice memory must be contiguous.
42 */
43 nothrow Slice!(Contiguous, packs, OutputType*) threshold(OutputType, InputType, SliceKind kind, size_t []packs)
44 (
45     Slice!(kind, packs, InputType*) input,
46     InputType lowThresh,
47     InputType highThresh,
48     Slice!(Contiguous, packs, OutputType*) prealloc = emptySlice!(packs, OutputType)
49 )
50 in
51 {
52     //TODO: consider leaving upper value, and not setting it to 1.
53     assert(lowThresh <= highThresh);
54     assert(!input.empty);
55 }
56 body
57 {
58     import std.math : approxEqual;
59     import std.traits : isFloatingPoint, isNumeric;
60 
61     static assert(isNumeric!OutputType, "Invalid output type - has to be numeric.");
62 
63     if (prealloc.shape != input.shape)
64     {
65         prealloc = uninitializedSlice!OutputType(input.shape);
66     }
67 
68     assert(input.structure.strides == prealloc.structure.strides,
69             "Input slice structure does not match with resulting buffer.");
70 
71     static if (isFloatingPoint!OutputType)
72         OutputType upvalue = 1.0;
73     else
74         OutputType upvalue = OutputType.max;
75 
76     auto p = zip!true(prealloc, input);
77 
78     if (lowThresh.approxEqual(highThresh))
79     {
80         p.each!((v)
81         {
82             v.a = cast(OutputType)(v.b <= lowThresh ? 0 : upvalue);
83         });
84     }
85     else
86     {
87         p.each!((v)
88         {
89             v.a = cast(OutputType)(v.b >= lowThresh && v.b <= highThresh ? upvalue : 0);
90         });
91     }
92 
93     return prealloc;
94 }
95 
96 /**
97 Convenience function for thresholding, where lower and upper bound values are the same.
98 
99 Calls threshold(slice, thresh, thresh, prealloc)
100 
101 Params:
102     input       = Input slice.
103     thresh      = Threshold value - any value lower than this will be set to 0, and higher to 1.
104     prealloc    = Optional pre-allocated slice buffer for output.
105 
106 Note:
107     Input and pre-allocated buffer slice, should be of same structure
108     (i.e. have same strides). If prealloc buffer is not given, and is
109     allocated anew, input slice memory must be contiguous.
110 */
111 nothrow Slice!(Contiguous, packs, OutputType*) threshold(OutputType, InputType, SliceKind kind, size_t []packs)
112 (
113     Slice!(kind, packs, InputType*) input,
114     InputType thresh,
115     Slice!(Contiguous, packs, OutputType*) prealloc = emptySlice!(packs, OutputType)
116 )
117 {
118     return threshold!(OutputType, InputType, N)(slice, thresh, thresh, prealloc);
119 }
120