1 /*
2 Copyright (c) 2013-2021 Timur Gafarov
3 
4 Boost Software License - Version 1.0 - August 17th, 2003
5 
6 Permission is hereby granted, free of charge, to any person or organization
7 obtaining a copy of the software and accompanying documentation covered by
8 this license (the "Software") to use, reproduce, display, distribute,
9 execute, and transmit the Software, and to prepare derivative works of the
10 Software, and to permit third-parties to whom the Software is furnished to
11 do so, all subject to the following:
12 
13 The copyright notices in the Software and this entire statement, including
14 the above license grant, this restriction and the following disclaimer,
15 must be included in all copies of the Software, in whole or in part, and
16 all derivative works of the Software, unless such copies or derivative
17 works are solely in the form of machine-executable object code generated by
18 a source language processor.
19 
20 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
23 SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
24 FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
25 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 DEALINGS IN THE SOFTWARE.
27 */
28 
29 /**
30  * 2D signal consisting of complex number data
31  *
32  * Copyright: Timur Gafarov 2013-2021.
33  * License: $(LINK2 boost.org/LICENSE_1_0.txt, Boost License 1.0).
34  * Authors: Timur Gafarov
35  */
36 module dlib.image.signal2d;
37 
38 private
39 {
40     import dlib.core.memory;
41     import dlib.math.vector;
42     import dlib.math.complex;
43     import dlib.math.fft;
44     import dlib.image.image;
45     import dlib.image.color;
46 }
47 
48 /// Signal domain
49 enum SignalDomain
50 {
51     Spatial,
52     Frequency
53 }
54 
55 /**
56  * 2D signal consisting of complex number data
57  */
58 class Signal2D
59 {
60     Complexf[] data;
61     uint width;
62     uint height;
63     SignalDomain domain = SignalDomain.Spatial;
64 
65     this(uint w, uint h)
66     {
67         width = w;
68         height = h;
69         data = New!(Complexf[])(width * height);
70     }
71 
72     this(SuperImage img, uint channel)
73     {
74         width = img.width;
75         height = img.height;
76         data = New!(Complexf[])(width * height);
77 
78         foreach(y; 0..height)
79         foreach(x; 0..width)
80         {
81             auto col = img[x, y];
82             size_t index = (y * width + x);
83             data[index].re = col.vec[channel];
84             data[index].im = 0.0f;
85         }
86     }
87     
88     ~this()
89     {
90         Delete(data);
91     }
92 
93     void fft()
94     {
95         foreach(y; 0..height)
96         foreach(x; 0..width)
97         {
98             if (((x + y) & 0x1) != 0)
99             {
100                 size_t index = (y * width + x);
101                 data[index].re = data[index].re * -1;
102                 data[index].im = data[index].im * -1;
103             }
104         }
105 
106         fft2(true);
107         domain = SignalDomain.Frequency;
108     }
109 
110     void fftInverse()
111     {
112         if (domain != SignalDomain.Frequency)
113             return;
114 
115         fft2(false);
116         domain = SignalDomain.Spatial;
117 
118         foreach(y; 0..height)
119         foreach(x; 0..width)
120         {
121             if (((x + y) & 0x1) != 0)
122             {
123                 size_t index = (y * width + x);
124                 data[index].re = data[index].re * -1;
125                 data[index].im = data[index].im * -1;
126             }
127         }
128     }
129 
130     void multiply(Signal2D img, Signal2D dest)
131     {
132         assert(img.width == width && img.height == height && dest.width == width && dest.height == height);
133 
134         dest.domain = domain;
135 
136         foreach(i, v; data)
137         {
138             dest.data[i] = v * img.data[i];
139         }
140     }
141     
142     void divide(Signal2D img, Signal2D dest)
143     {
144         assert(img.width == width && img.height == height && dest.width == width && dest.height == height);
145 
146         dest.domain = domain;
147 
148         foreach(i, v; data)
149         {
150             if (img.data[i].re == 0.0f)
151                 dest.data[i] = v;
152             else
153                 dest.data[i] = v / img.data[i];
154         }
155     }
156 
157     Complexf opIndex(int x, int y)
158     {
159         while(x >= width) x = width-1;
160         while(y >= height) y = height-1;
161         while(x < 0) x = 0;
162         while(y < 0) y = 0;
163 
164         return data[y * width + x];
165     }
166 
167     void fft2(bool forward)
168     {
169         // process rows
170         Complexf[] row = New!(Complexf[])(width);
171         for (int y = 0; y < height; y++)
172         {
173             for (int x = 0; x < width; x++)
174             {
175                 auto index = y * width + x;
176                 row[x] = data[index];
177             }
178 
179             fastFourierTransform(row, forward);
180 
181             for (int x = 0; x < width; x++)
182             {
183                 auto index = y * width + x;
184                 data[index] = row[x];
185             }
186         }
187 
188         // process columns
189         Complexf[] col = New!(Complexf[])(height);
190         for (int x = 0; x < width; x++)
191         {
192             for (int y = 0; y < height; y++)
193             {
194                 auto index = y * width + x;
195                 col[y] = data[index];
196             }
197 
198             fastFourierTransform(col, forward);
199 
200             for (int y = 0; y < height; y++)
201             {
202                 auto index = y * width + x;
203                 data[index] = col[y];
204             }
205         }
206         
207         Delete(row);
208         Delete(col);
209     }
210 }
211 
212 /// Convert Signal2D to SuperImage
213 void signalToImage(Signal2D chRed, Signal2D chGreen, Signal2D chBlue, SuperImage img)
214 {
215     float maxIntensityR = 0.0f;
216     float maxIntensityG = 0.0f;
217     float maxIntensityB = 0.0f;
218     Vector3f[] fpImage = New!(Vector3f[])(img.width * img.height);
219 
220     for(int i = 0; i < chRed.data.length; i++)
221     {
222         float mr, mg, mb;
223 
224         mr = chRed.data[i].magnitude;
225         if (mr > maxIntensityR)
226             maxIntensityR = mr;
227         mg = chGreen.data[i].magnitude;
228         if (mg > maxIntensityG)
229             maxIntensityG = mg;
230         mb = chBlue.data[i].magnitude;
231         if (mb > maxIntensityB)
232             maxIntensityB = mb;
233 
234         fpImage[i] = Vector3f(mr, mg, mb);
235     }
236 
237     foreach(ref v; fpImage)
238     {
239         v.r /= maxIntensityR;
240         v.g /= maxIntensityG;
241         v.b /= maxIntensityB;
242     }
243 
244     foreach(y; 0..img.height)
245     foreach(x; 0..img.width)
246     {
247         size_t index = (y * img.width + x);
248         Vector3f m = fpImage[index];
249         img[x, y] = Color4f(m);
250     }
251 
252     Delete(fpImage);
253 }