1 /**
2 Module implements various algorithms used often in computer vision.
3 
4 $(DL Module contains:
5     $(DD 
6             $(LINK2 #norm,norm)
7             $(LINK2 #normalized,normalized)
8             $(LINK2 #scaled,scaled)
9             $(LINK2 #ranged,ranged)
10     )
11 )
12 
13 Copyright: Copyright Relja Ljubobratovic 2016.
14 
15 Authors: Relja Ljubobratovic
16 
17 License: $(LINK3 http://www.boost.org/LICENSE_1_0.txt, Boost Software License - Version 1.0).
18 */
19 
20 module dcv.core.algorithm;
21 
22 import std.traits : isNumeric, isFloatingPoint;
23 
24 import mir.ndslice.slice;
25 import mir.ndslice.algorithm : reduce, each;
26 import mir.math.common;
27 
28 /**
29 Scale tensor values.
30 
31 Params:
32     tensor = Input tensor.
33     alpha = Multiplier value.
34     beta = Offset value.
35 
36 Performs value modification of tensor elements using following formula:
37 ----
38 ref output = alpha * (input) + beta;
39 ----
40 
41 Returns:
42     Scaled input tensor.
43     
44 */
45 nothrow @nogc auto scaled(Scalar, Tensor)(Tensor tensor, Scalar alpha = 1, Scalar beta = 0) if (isNumeric!Scalar)
46 in
47 {
48     static assert(isSlice!Tensor, "Input tensor has to be of type mir.ndslice.slice.Slice.");
49 }
50 body
51 {
52     tensor.each!((ref v) { v = cast(DeepElementType!Tensor)(alpha * (v) + beta); });
53     return tensor;
54 }
55 
56 nothrow unittest
57 {
58     import std.math : approxEqual;
59 
60     auto t = tensor1();
61     auto ts = t.scaled(10.0f, 2.0f);
62 
63     assert(t.iterator == ts.iterator);
64 
65     assert(approxEqual(ts[0], 12.0f));
66     assert(approxEqual(ts[1], 22.0f));
67     assert(approxEqual(ts[2], 32.0f));
68 }
69 
70 /**
71 In-place tensor scaling to fit given value range.
72 
73 Params:
74     tensor = Input tensor.
75     minValue = Minimal value output tensor should contain.
76     maxValue = Maximal value output tensor should contain.
77 
78 */
79 nothrow @nogc auto ranged(Scalar, Tensor)(Tensor tensor,
80         Scalar minValue = 0, Scalar maxValue = 1) if (isNumeric!Scalar)
81 in
82 {
83     static assert(isSlice!Tensor, "Input tensor has to be of type mir.ndslice.slice.Slice.");
84 }
85 body
86 {
87     alias T = DeepElementType!Tensor;
88 
89     static if (isFloatingPoint!T)
90     {
91         import mir.math.common : fmin, fmax;
92         auto _max = reduce!fmax(T.min_normal, tensor);
93         auto _min = reduce!fmin(T.max, tensor);
94     }
95     else
96     {
97         import mir.utility : min, max;
98         auto _max = reduce!max(T.min, tensor);
99         auto _min = reduce!min(T.max, tensor);
100     }
101 
102     auto rn_val = _max - _min;
103     auto sc_val = maxValue - minValue;
104 
105     tensor.each!((ref a) { a = cast(T)(sc_val * ((a - _min) / rn_val) + minValue); });
106 
107     return tensor;
108 }
109 
110 unittest
111 {
112     immutable smin = -10.0f;
113     immutable smax = 15.0f;
114 
115     auto t1 = tensor1();
116     auto t2 = tensor2();
117     auto t3 = tensor3();
118 
119     auto t1r = t1.ranged(smin, smax);
120     auto t2r = t2.ranged(smin, smax);
121     auto t3r = t3.ranged(smin, smax);
122 
123     assert(t1.iterator == t1r.iterator);
124     assert(t2.iterator == t2r.iterator);
125     assert(t3.iterator == t3r.iterator);
126 
127 }
128 
129 nothrow unittest
130 {
131     import std.math : approxEqual;
132 
133     import mir.utility;
134 
135     immutable smin = -10.0f;
136     immutable smax = 15.0f;
137 
138     auto t1 = tensor1();
139     auto t2 = tensor2();
140     auto t3 = tensor3();
141 
142     auto t1r = t1.ranged(smin, smax);
143     auto t2r = t2.ranged(smin, smax);
144     auto t3r = t3.ranged(smin, smax);
145 
146     assert(approxEqual(reduce!min(float.max, t1r), smin));
147     assert(approxEqual(reduce!max(float.min_normal, t1r), smax));
148 
149     assert(approxEqual(reduce!min(float.max, t2r), smin));
150     assert(approxEqual(reduce!max(float.min_normal, t2r), smax));
151 
152     assert(approxEqual(reduce!min(float.max, t3r), smin));
153     assert(approxEqual(reduce!max(float.min_normal, t3r), smax));
154 }
155 
156 version (unittest)
157 {
158     auto tensor1()
159     {
160         return [1.0f, 2.0f, 3.0f].sliced(3);
161     }
162 
163     auto tensor2()
164     {
165         return [1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f].sliced(3, 3);
166     }
167 
168     auto tensor3()
169     {
170         return [1.0f, 2.0f, 3.0f,       4.0f, 5.0f, 6.0f,       7.0f, 8.0f, 9.0f, 
171                10.0f, 11.0f, 12.0f,     13.0f, 14.0f, 15.0f,    16.0f, 17.0f, 18.0f, 
172                19.0f, 20.0f, 21.0f,     22.0f, 23.0f, 24.0f,    25.0f, 26.0f, 27.0f].sliced(3, 3, 3);
173     }
174 }