Commit fdc7f32c10427a43e2cbbe6a7807455f4ca6c007
1 parent
da132b67
Added a coupled wave library containing code to implement Bryn's homogeneous layered model
Showing
2 changed files
with
384 additions
and
17 deletions
Show diff stats
1 | +# -*- coding: utf-8 -*- | ||
2 | +""" | ||
3 | +Created on Thu Sep 6 22:14:36 2018 | ||
4 | + | ||
5 | +@author: david | ||
6 | +""" | ||
7 | + | ||
8 | +import numpy as np | ||
9 | +import matplotlib.pyplot as plt | ||
10 | + | ||
11 | +#evaluate a plane wave for all points in R = [Rx, Ry, Rz] | ||
12 | +# E0 [Ex, Ey, Ez] is the electric field vector | ||
13 | +# k is the k vector | ||
14 | +# n is the refractive index of the material | ||
15 | +# R is a list of coordinates in x, y, and z | ||
16 | +def planewave2field(E0, k, n, R): | ||
17 | + knr = n * (k[0] * R[0] + k[1] * R[1] + k[2] + R[2]) | ||
18 | + exp_2pknr = np.exp(1j * 2 * np.pi * knr) | ||
19 | + Ex = E0[0] * exp_2pknr | ||
20 | + Ey = E0[1] * exp_2pknr | ||
21 | + Ez = E0[2] * exp_2pknr | ||
22 | + | ||
23 | + return [Ex, Ey, Ez] | ||
24 | + | ||
25 | +def orthogonalize(E, k): | ||
26 | + s = np.cross(E, k) | ||
27 | + return np.cross(k, s) | ||
28 | + | ||
29 | +class layersample: | ||
30 | + | ||
31 | + #generate a layered sample based on a list of layer refractive indices | ||
32 | + # layer_ri is a list of complex refractive indices for each layer | ||
33 | + # z is the list of boundary positions along z | ||
34 | + def __init__(self, layer_ri, z): | ||
35 | + self.n = np.array(layer_ri).astype(np.complex128) | ||
36 | + self.z = np.array(z) | ||
37 | + | ||
38 | + #calculate the index of the field component associated with a layer | ||
39 | + # l is the layer index [0, L) | ||
40 | + # c is the component (x=0, y=1, z=2) | ||
41 | + # d is the direction (0 = transmission, 1 = reflection) | ||
42 | + def idx(self, l, c, d): | ||
43 | + i = l * 6 + d * 3 + c - 3 | ||
44 | + print(i) | ||
45 | + return i | ||
46 | + | ||
47 | + #create a matrix for a single plane wave specified by k and E | ||
48 | + def solve(self, k, E): | ||
49 | + | ||
50 | + #calculate the wavenumber | ||
51 | + kn = np.linalg.norm(k) | ||
52 | + | ||
53 | + #s is the plane wave direction scaled by the refractive index | ||
54 | + s = k / kn * self.n[0] | ||
55 | + | ||
56 | + #allocate space for the matrix | ||
57 | + L = len(self.n) | ||
58 | + M = np.zeros((6*(L-1), 6*(L-1)), dtype=np.complex128) | ||
59 | + | ||
60 | + #allocate space for the RHS vector | ||
61 | + b = np.zeros(6*(L-1), dtype=np.complex128) | ||
62 | + | ||
63 | + #initialize a counter for the equation number | ||
64 | + ei = 0 | ||
65 | + | ||
66 | + #calculate the sz component for each layer | ||
67 | + self.sz = np.zeros(L, dtype=np.complex128) | ||
68 | + for l in range(L): | ||
69 | + self.sz[l] = np.sqrt(self.n[l]**2 - s[0]**2 - s[1]**2) | ||
70 | + | ||
71 | + #set constraints based on Gauss' law | ||
72 | + for l in range(0, L): | ||
73 | + #sz = np.sqrt(self.n[l]**2 - s[0]**2 - s[1]**2) | ||
74 | + | ||
75 | + #set the upward components for each layer | ||
76 | + # note that layer L-1 does not have a downward component | ||
77 | + # David I, Equation 7 | ||
78 | + if l != L-1: | ||
79 | + M[ei, self.idx(l, 0, 1)] = s[0] | ||
80 | + M[ei, self.idx(l, 1, 1)] = s[1] | ||
81 | + M[ei, self.idx(l, 2, 1)] = -self.sz[l] | ||
82 | + ei = ei+1 | ||
83 | + | ||
84 | + #set the downward components for each layer | ||
85 | + # note that layer 0 does not have a downward component | ||
86 | + # Davis I, Equation 6 | ||
87 | + if l != 0: | ||
88 | + M[ei, self.idx(l, 0, 0)] = s[0] | ||
89 | + M[ei, self.idx(l, 1, 0)] = s[1] | ||
90 | + M[ei, self.idx(l, 2, 0)] = self.sz[l] | ||
91 | + ei = ei+1 | ||
92 | + | ||
93 | + #enforce a continuous field across boundaries | ||
94 | + for l in range(1, L): | ||
95 | + sz0 = self.sz[l-1] | ||
96 | + sz1 = self.sz[l] | ||
97 | + A = np.exp(1j * kn * sz0 * (self.z[l] - self.z[l-1])) | ||
98 | + if l < L - 1: | ||
99 | + B = np.exp(-1j * kn * sz1 * (self.z[l] - self.z[l+1])) | ||
100 | + | ||
101 | + | ||
102 | + #if this is the second layer, use the simplified equations that account for the incident field | ||
103 | + if l == 1: | ||
104 | + M[ei, self.idx(0, 0, 1)] = 1 | ||
105 | + M[ei, self.idx(1, 0, 0)] = -1 | ||
106 | + if L > 2: | ||
107 | + M[ei, self.idx(1, 0, 1)] = -B | ||
108 | + | ||
109 | + b[ei] = -A * E[0] | ||
110 | + ei = ei + 1 | ||
111 | + | ||
112 | + M[ei, self.idx(0, 1, 1)] = 1 | ||
113 | + M[ei, self.idx(1, 1, 0)] = -1 | ||
114 | + if L > 2: | ||
115 | + M[ei, self.idx(1, 1, 1)] = -B | ||
116 | + b[ei] = -A * E[1] | ||
117 | + ei = ei + 1 | ||
118 | + | ||
119 | + M[ei, self.idx(0, 2, 1)] = s[1] | ||
120 | + M[ei, self.idx(0, 1, 1)] = sz0 | ||
121 | + M[ei, self.idx(1, 2, 0)] = -s[1] | ||
122 | + M[ei, self.idx(1, 1, 0)] = sz1 | ||
123 | + if L > 2: | ||
124 | + M[ei, self.idx(1, 2, 1)] = -B*s[1] | ||
125 | + M[ei, self.idx(1, 1, 1)] = -B*sz1 | ||
126 | + b[ei] = A * sz0 * E[1] - A * s[1]*E[2] | ||
127 | + ei = ei + 1 | ||
128 | + | ||
129 | + M[ei, self.idx(0, 0, 1)] = -sz0 | ||
130 | + M[ei, self.idx(0, 2, 1)] = -s[0] | ||
131 | + M[ei, self.idx(1, 0, 0)] = -sz1 | ||
132 | + M[ei, self.idx(1, 2, 0)] = s[0] | ||
133 | + if L > 2: | ||
134 | + M[ei, self.idx(1, 0, 1)] = B*sz1 | ||
135 | + M[ei, self.idx(1, 2, 1)] = B*s[0] | ||
136 | + b[ei] = A * s[0] * E[2] - A * sz0 * E[0] | ||
137 | + ei = ei + 1 | ||
138 | + | ||
139 | + #if this is the last layer, use the simplified equations that exclude reflections from the last layer | ||
140 | + elif l == L-1: | ||
141 | + M[ei, self.idx(l-1, 0, 0)] = A | ||
142 | + M[ei, self.idx(l-1, 0, 1)] = 1 | ||
143 | + M[ei, self.idx(l, 0, 0)] = -1 | ||
144 | + ei = ei + 1 | ||
145 | + | ||
146 | + M[ei, self.idx(l-1, 1, 0)] = A | ||
147 | + M[ei, self.idx(l-1, 1, 1)] = 1 | ||
148 | + M[ei, self.idx(l, 1, 0)] = -1 | ||
149 | + ei = ei + 1 | ||
150 | + | ||
151 | + M[ei, self.idx(l-1, 2, 0)] = A*s[1] | ||
152 | + M[ei, self.idx(l-1, 1, 0)] = -A*sz0 | ||
153 | + M[ei, self.idx(l-1, 2, 1)] = s[1] | ||
154 | + M[ei, self.idx(l-1, 1, 1)] = sz0 | ||
155 | + M[ei, self.idx(l, 2, 0)] = -s[1] | ||
156 | + M[ei, self.idx(l, 1, 0)] = sz1 | ||
157 | + ei = ei + 1 | ||
158 | + | ||
159 | + M[ei, self.idx(l-1, 0, 0)] = A*sz0 | ||
160 | + M[ei, self.idx(l-1, 2, 0)] = -A*s[0] | ||
161 | + M[ei, self.idx(l-1, 0, 1)] = -sz0 | ||
162 | + M[ei, self.idx(l-1, 2, 1)] = -s[0] | ||
163 | + M[ei, self.idx(l, 0, 0)] = -sz1 | ||
164 | + M[ei, self.idx(l, 2, 0)] = s[0] | ||
165 | + ei = ei + 1 | ||
166 | + #otherwise use the full set of boundary conditions | ||
167 | + else: | ||
168 | + M[ei, self.idx(l-1, 0, 0)] = A | ||
169 | + M[ei, self.idx(l-1, 0, 1)] = 1 | ||
170 | + M[ei, self.idx(l, 0, 0)] = -1 | ||
171 | + M[ei, self.idx(l, 0, 1)] = -B | ||
172 | + ei = ei + 1 | ||
173 | + | ||
174 | + M[ei, self.idx(l-1, 1, 0)] = A | ||
175 | + M[ei, self.idx(l-1, 1, 1)] = 1 | ||
176 | + M[ei, self.idx(l, 1, 0)] = -1 | ||
177 | + M[ei, self.idx(l, 1, 1)] = -B | ||
178 | + ei = ei + 1 | ||
179 | + | ||
180 | + M[ei, self.idx(l-1, 2, 0)] = A*s[1] | ||
181 | + M[ei, self.idx(l-1, 1, 0)] = -A*sz0 | ||
182 | + M[ei, self.idx(l-1, 2, 1)] = s[1] | ||
183 | + M[ei, self.idx(l-1, 1, 1)] = sz0 | ||
184 | + M[ei, self.idx(l, 2, 0)] = -s[1] | ||
185 | + M[ei, self.idx(l, 1, 0)] = sz1 | ||
186 | + M[ei, self.idx(l, 2, 1)] = -B*s[1] | ||
187 | + M[ei, self.idx(l, 1, 1)] = -B*sz1 | ||
188 | + ei = ei + 1 | ||
189 | + | ||
190 | + M[ei, self.idx(l-1, 0, 0)] = A*sz0 | ||
191 | + M[ei, self.idx(l-1, 2, 0)] = -A*s[0] | ||
192 | + M[ei, self.idx(l-1, 0, 1)] = -sz0 | ||
193 | + M[ei, self.idx(l-1, 2, 1)] = -s[0] | ||
194 | + M[ei, self.idx(l, 0, 0)] = -sz1 | ||
195 | + M[ei, self.idx(l, 2, 0)] = s[0] | ||
196 | + M[ei, self.idx(l, 0, 1)] = B*sz1 | ||
197 | + M[ei, self.idx(l, 2, 1)] = B*s[0] | ||
198 | + ei = ei + 1 | ||
199 | + | ||
200 | + #store the matrix and RHS vector (for debugging) | ||
201 | + self.M = M | ||
202 | + self.b = b | ||
203 | + | ||
204 | + #evaluate the linear system | ||
205 | + P = np.linalg.solve(M, b) | ||
206 | + | ||
207 | + #save the results (also for debugging) | ||
208 | + self.P = P | ||
209 | + | ||
210 | + #store the coefficients for each layer | ||
211 | + self.Ptrans = [] | ||
212 | + self.Prefl = [] | ||
213 | + for l in range(L): | ||
214 | + if l == 0: | ||
215 | + self.Ptrans.append((E[0], E[1], E[2])) | ||
216 | + else: | ||
217 | + px = P[self.idx(l, 0, 0)] | ||
218 | + py = P[self.idx(l, 1, 0)] | ||
219 | + pz = P[self.idx(l, 2, 0)] | ||
220 | + self.Ptrans.append((px, py, pz)) | ||
221 | + | ||
222 | + if l == L-1: | ||
223 | + self.Prefl.append((0, 0, 0)) | ||
224 | + else: | ||
225 | + px = P[self.idx(l, 0, 1)] | ||
226 | + py = P[self.idx(l, 1, 1)] | ||
227 | + pz = P[self.idx(l, 2, 1)] | ||
228 | + self.Prefl.append((px, py, pz)) | ||
229 | + | ||
230 | + #this converts the coefficients into a NumPy array for convenience later on | ||
231 | + self.Ptrans = np.array(self.Ptrans) | ||
232 | + self.Prefl = np.array(self.Prefl) | ||
233 | + | ||
234 | + #store values required for evaluation | ||
235 | + #store k | ||
236 | + self.k = kn | ||
237 | + | ||
238 | + #store sx and sy | ||
239 | + self.s = np.array([s[0], s[1]]) | ||
240 | + | ||
241 | + self.solved = True | ||
242 | + | ||
243 | + #evaluate a solved homogeneous substrate | ||
244 | + def evaluate(self, X, Y, Z): | ||
245 | + | ||
246 | + if not self.solved: | ||
247 | + print("ERROR: the layered substrate hasn't been solved") | ||
248 | + return | ||
249 | + | ||
250 | + #this code is a bit cumbersome and could probably be optimized | ||
251 | + # Basically, it vectorizes everything by creating an image | ||
252 | + # such that the value at each pixel is the corresponding layer | ||
253 | + # that the pixel resides in. That index is then used to calculate | ||
254 | + # the field within the layer | ||
255 | + | ||
256 | + #allocate space for layer indices | ||
257 | + LI = np.zeros(Z.shape, dtype=np.int) | ||
258 | + | ||
259 | + #find the layer index for each sample point | ||
260 | + L = len(self.z) | ||
261 | + LI[Z < self.z[0]] = 0 | ||
262 | + for l in range(L-1): | ||
263 | + idx = np.logical_and(Z > self.z[l], Z <= self.z[l+1]) | ||
264 | + LI[idx] = l | ||
265 | + LI[Z > self.z[-1]] = L - 1 | ||
266 | + | ||
267 | + #calculate the appropriate phase shift for the wave transmitted through the layer | ||
268 | + Ph_t = np.exp(1j * self.k * self.sz[LI] * (Z - self.z[LI])) | ||
269 | + | ||
270 | + #calculate the appropriate phase shift for the wave reflected off of the layer boundary | ||
271 | + LIp = LI + 1 | ||
272 | + LIp[LIp >= L] = 0 | ||
273 | + Ph_r = np.exp(-1j * self.k * self.sz[LI] * (Z - self.z[LIp])) | ||
274 | + Ph_r[LI >= L-1] = 0 | ||
275 | + | ||
276 | + #calculate the phase shift based on the X and Y positions | ||
277 | + Ph_xy = np.exp(1j * self.k * (self.s[0] * X + self.s[1] * Y)) | ||
278 | + | ||
279 | + #apply the phase shifts | ||
280 | + Et = self.Ptrans[LI] * Ph_t[:, :, None] | ||
281 | + Er = self.Prefl[LI] * Ph_r[:, :, None] | ||
282 | + | ||
283 | + #add everything together coherently | ||
284 | + E = (Et + Er) * Ph_xy[:, :, None] | ||
285 | + | ||
286 | + #return the electric field | ||
287 | + return E | ||
288 | + | ||
289 | + | ||
290 | +#This sample code produces a field similar to Figure 2 in Davis et al., 2010 | ||
291 | +#set the material properties | ||
292 | +depths = [-50, -15, 15, 40] | ||
293 | +n = [1.0, 1.4+1j*0.05, 1.4, 1.0] | ||
294 | + | ||
295 | +#create a layered sample | ||
296 | +layers = layersample(n, depths) | ||
297 | + | ||
298 | +#set the input light parameters | ||
299 | +d = np.array([0.2, 0, 1]) | ||
300 | +d = d / np.linalg.norm(d) | ||
301 | +l = 5.0 | ||
302 | +k = 2 * np.pi / l * d | ||
303 | +E0 = [0, 1, 0] | ||
304 | +E0 = orthogonalize(E0, d) | ||
305 | + | ||
306 | +#solve for the substrate field | ||
307 | +layers.solve(k, E0) | ||
308 | + | ||
309 | +#set the simulation domain | ||
310 | +N = 512 | ||
311 | +M = 1024 | ||
312 | +D = [-50, 80] | ||
313 | + | ||
314 | +x = np.linspace(D[0], D[1], N) | ||
315 | +z = np.linspace(D[0], D[1], M) | ||
316 | +[X, Z] = np.meshgrid(x, z) | ||
317 | +Y = np.zeros(X.shape) | ||
318 | +E = layers.evaluate(X, Y, Z) | ||
319 | + | ||
320 | +Er = np.real(E) | ||
321 | +I = Er[..., 0] ** 2 + Er[..., 1] **2 + Er[..., 2] ** 2 | ||
322 | +plt.figure() | ||
323 | +plt.set_cmap("afmhot") | ||
324 | +plt.imshow(Er[..., 1]) |
stim/image/image.h
@@ -33,6 +33,7 @@ class image{ | @@ -33,6 +33,7 @@ class image{ | ||
33 | 33 | ||
34 | T* img; //pointer to the image data (interleaved RGB for color) | 34 | T* img; //pointer to the image data (interleaved RGB for color) |
35 | size_t R[3]; | 35 | size_t R[3]; |
36 | + bool interleaved = true; //by default the data is interleaved | ||
36 | 37 | ||
37 | inline size_t X() const { return R[1]; } | 38 | inline size_t X() const { return R[1]; } |
38 | inline size_t Y() const { return R[2]; } | 39 | inline size_t Y() const { return R[2]; } |
@@ -73,13 +74,24 @@ class image{ | @@ -73,13 +74,24 @@ class image{ | ||
73 | 74 | ||
74 | #ifdef USING_OPENCV | 75 | #ifdef USING_OPENCV |
75 | int cv_type(){ | 76 | int cv_type(){ |
76 | - if(typeid(T) == typeid(unsigned char)) return CV_MAKETYPE(CV_8U, (int)C()); | ||
77 | - if(typeid(T) == typeid(char)) return CV_MAKETYPE(CV_8S, (int)C()); | ||
78 | - if(typeid(T) == typeid(unsigned short)) return CV_MAKETYPE(CV_16U, (int)C()); | ||
79 | - if(typeid(T) == typeid(short)) return CV_MAKETYPE(CV_16S, (int)C()); | ||
80 | - if(typeid(T) == typeid(int)) return CV_MAKETYPE(CV_32S, (int)C()); | ||
81 | - if(typeid(T) == typeid(float)) return CV_MAKETYPE(CV_32F, (int)C()); | ||
82 | - if(typeid(T) == typeid(double)) return CV_MAKETYPE(CV_64F, (int)C()); | 77 | + if (C() == 1 || C() == 3) { //if the image has 1 or 3 channels, this will work |
78 | + if (typeid(T) == typeid(unsigned char)) return CV_MAKETYPE(CV_8U, (int)C()); | ||
79 | + if (typeid(T) == typeid(char)) return CV_MAKETYPE(CV_8S, (int)C()); | ||
80 | + if (typeid(T) == typeid(unsigned short)) return CV_MAKETYPE(CV_16U, (int)C()); | ||
81 | + if (typeid(T) == typeid(short)) return CV_MAKETYPE(CV_16S, (int)C()); | ||
82 | + if (typeid(T) == typeid(int)) return CV_MAKETYPE(CV_32S, (int)C()); | ||
83 | + if (typeid(T) == typeid(float)) return CV_MAKETYPE(CV_32F, (int)C()); | ||
84 | + if (typeid(T) == typeid(double)) return CV_MAKETYPE(CV_64F, (int)C()); | ||
85 | + } | ||
86 | + else if (C() == 4) { //OpenCV only supports saving BGR images - 4-channel isn't supported | ||
87 | + if (typeid(T) == typeid(unsigned char)) return CV_MAKETYPE(CV_8U, 3); | ||
88 | + if (typeid(T) == typeid(char)) return CV_MAKETYPE(CV_8S, 3); | ||
89 | + if (typeid(T) == typeid(unsigned short)) return CV_MAKETYPE(CV_16U, 3); | ||
90 | + if (typeid(T) == typeid(short)) return CV_MAKETYPE(CV_16S, 3); | ||
91 | + if (typeid(T) == typeid(int)) return CV_MAKETYPE(CV_32S, 3); | ||
92 | + if (typeid(T) == typeid(float)) return CV_MAKETYPE(CV_32F, 3); | ||
93 | + if (typeid(T) == typeid(double)) return CV_MAKETYPE(CV_64F, 3); | ||
94 | + } | ||
83 | 95 | ||
84 | std::cout<<"ERROR in stim::image::cv_type - no valid data type found"<<std::endl; | 96 | std::cout<<"ERROR in stim::image::cv_type - no valid data type found"<<std::endl; |
85 | exit(1); | 97 | exit(1); |
@@ -351,15 +363,37 @@ public: | @@ -351,15 +363,37 @@ public: | ||
351 | } | 363 | } |
352 | #ifdef USING_OPENCV | 364 | #ifdef USING_OPENCV |
353 | //OpenCV uses an interleaved format, so convert first and then output | 365 | //OpenCV uses an interleaved format, so convert first and then output |
354 | - T* buffer = (T*) malloc(bytes()); | ||
355 | - | ||
356 | - if(C() == 1) | 366 | + |
367 | + //single-channel image | ||
368 | + if (C() == 1) { | ||
369 | + T* buffer = (T*)malloc(bytes()); | ||
357 | memcpy(buffer, img, bytes()); | 370 | memcpy(buffer, img, bytes()); |
358 | - else if(C() == 3) | 371 | + cv::Mat cvImage((int)Y(), (int)X(), cv_type(), buffer); |
372 | + cv::imwrite(filename, cvImage); | ||
373 | + free(buffer); | ||
374 | + } | ||
375 | + //3-channel image | ||
376 | + else if (C() == 3) { | ||
377 | + T* buffer = (T*)malloc(bytes()); | ||
359 | get_interleaved_bgr(buffer); | 378 | get_interleaved_bgr(buffer); |
360 | - cv::Mat cvImage((int)Y(), (int)X(), cv_type(), buffer); | ||
361 | - cv::imwrite(filename, cvImage); | ||
362 | - free(buffer); | 379 | + cv::Mat cvImage((int)Y(), (int)X(), cv_type(), buffer); |
380 | + cv::imwrite(filename, cvImage); | ||
381 | + free(buffer); | ||
382 | + } | ||
383 | + //ARGB image | ||
384 | + else if (C() == 4) { | ||
385 | + size_t channelsize = X() * Y() * sizeof(T); | ||
386 | + T* buffer = (T*)malloc(bytes() - channelsize); | ||
387 | + get_interleaved_bgr(buffer); | ||
388 | + cv::Mat cvImage((int)Y(), (int)X(), cv_type(), buffer); | ||
389 | + cv::imwrite(filename, cvImage); | ||
390 | + free(buffer); | ||
391 | + } | ||
392 | + else { | ||
393 | + throw std::runtime_error("ERROR: number of channels not supported for image saving."); | ||
394 | + } | ||
395 | + | ||
396 | + | ||
363 | #else | 397 | #else |
364 | if (file.extension() == "ppm") | 398 | if (file.extension() == "ppm") |
365 | save_netpbm(filename); | 399 | save_netpbm(filename); |
@@ -419,12 +453,21 @@ public: | @@ -419,12 +453,21 @@ public: | ||
419 | } | 453 | } |
420 | 454 | ||
421 | void get_interleaved_bgr(T* data){ | 455 | void get_interleaved_bgr(T* data){ |
422 | - | ||
423 | //for each channel | 456 | //for each channel |
457 | + T* source; | ||
458 | + if (C() == 3) { | ||
459 | + source = img; //if the image has 3 channels, interleave all three | ||
460 | + } | ||
461 | + else if (C() == 4) { | ||
462 | + source = img + X() * Y(); //if the image has 4 channels, skip the alpha channel | ||
463 | + } | ||
464 | + else { | ||
465 | + throw std::runtime_error("ERROR: a BGR image must be 3 or 4 channels"); //throw an error if any other number of channels is provided | ||
466 | + } | ||
424 | for(size_t y = 0; y < Y(); y++){ | 467 | for(size_t y = 0; y < Y(); y++){ |
425 | for(size_t x = 0; x < X(); x++){ | 468 | for(size_t x = 0; x < X(); x++){ |
426 | - for(size_t c = 0; c < C(); c++){ | ||
427 | - data[y * X() * C() + x * C() + (2-c)] = img[idx(x, y, c)]; | 469 | + for(size_t c = 0; c < 3; c++){ |
470 | + data[y * X() * 3 + x * 3 + (2-c)] = source[y*3*R[1] + x*3 + c]; | ||
428 | } | 471 | } |
429 | } | 472 | } |
430 | } | 473 | } |