SourceXtractorPlusPlus 1.0.3
SourceXtractor++, the next generation SExtractor
Loading...
Searching...
No Matches
ImageInterfaceTraits.cpp
Go to the documentation of this file.
1/*
2 * ImageInterfaceTraits.cpp
3 *
4 * Created on: Aug 16, 2019
5 * Author: mschefer
6 */
7
8#include <iostream>
9
11
12namespace SourceXtractor {
13
15constexpr float PI = boost::math::constants::pi<float>();
16
17static void makeLanczos2Kernel(float pos, float *kernel, const float threshold) {
18 float x, val, sinx1, cosx1;
19
20 if (pos < threshold && pos > -threshold) {
21 *(kernel++) = 0.0;
22 *(kernel++) = 1.0;
23 *(kernel++) = 0.0;
24 *kernel = 0.0;
25 }
26 else {
27 x = -PI / 2.0 * (pos + 1.0);
28 sincosf(x, &sinx1, &cosx1);
29 val = (*(kernel++) = sinx1 / (x * x));
30 x += PI / 2.0;
31 val += (*(kernel++) = -cosx1 / (x * x));
32 x += PI / 2.0;
33 val += (*(kernel++) = -sinx1 / (x * x));
34 x += PI / 2.0;
35 val += (*kernel = cosx1 / (x * x));
36 val = 1.0 / val;
37 *(kernel--) *= val;
38 *(kernel--) *= val;
39 *(kernel--) *= val;
40 *kernel *= val;
41 }
42}
43
44static void makeLanczos3Kernel(float pos, float *kernel, const float threshold) {
45 float x, val, sinx1, sinx2, sinx3, cosx1;
46
47 if (pos < threshold && pos > -threshold) {
48 *(kernel++) = 0.0;
49 *(kernel++) = 0.0;
50 *(kernel++) = 1.0;
51 *(kernel++) = 0.0;
52 *(kernel++) = 0.0;
53 *kernel = 0.0;
54 }
55 else {
56 x = -PI / 3.0 * (pos + 2.0);
57 sincosf(x, &sinx1, &cosx1);
58 val = (*(kernel++) = sinx1 / (x * x));
59 x += PI / 3.0;
60 val += (*(kernel++) = (sinx2 = -0.5 * sinx1 - 0.866025403785 * cosx1) / (x * x));
61 x += PI / 3.0;
62 val += (*(kernel++) = (sinx3 = -0.5 * sinx1 + 0.866025403785 * cosx1) / (x * x));
63 x += PI / 3.0;
64 val += (*(kernel++) = sinx1 / (x * x));
65 x += PI / 3.0;
66 val += (*(kernel++) = sinx2 / (x * x));
67 x += PI / 3.0;
68 val += (*kernel = sinx3 / (x * x));
69 val = 1.0 / val;
70 *(kernel--) *= val;
71 *(kernel--) *= val;
72 *(kernel--) *= val;
73 *(kernel--) *= val;
74 *(kernel--) *= val;
75 *kernel *= val;
76 }
77}
78
79static void makeLanczos4Kernel(float pos, float *kernel, const float threshold) {
80 float x, val, sinx1, sinx2, sinx3, cosx1;
81
82 if (pos < threshold && pos > -threshold) {
83 *(kernel++) = 0.0;
84 *(kernel++) = 0.0;
85 *(kernel++) = 0.0;
86 *(kernel++) = 1.0;
87 *(kernel++) = 0.0;
88 *(kernel++) = 0.0;
89 *(kernel++) = 0.0;
90 *kernel = 0.0;
91 }
92 else {
93 x = -PI / 4.0 * (pos + 3.0);
94 sincosf(x, &sinx1, &cosx1);
95 val = (*(kernel++) = sinx1 / (x * x));
96 x += PI / 4.0;
97 val += (*(kernel++) = -(sinx2 = 0.707106781186 * (sinx1 + cosx1)) / (x * x));
98 x += PI / 4.0;
99 val += (*(kernel++) = cosx1 / (x * x));
100 x += PI / 4.0;
101 val += (*(kernel++) = -(sinx3 = 0.707106781186 * (cosx1 - sinx1)) / (x * x));
102 x += PI / 4.0;
103 val += (*(kernel++) = -sinx1 / (x * x));
104 x += PI / 4.0;
105 val += (*(kernel++) = sinx2 / (x * x));
106 x += PI / 4.0;
107 val += (*(kernel++) = -cosx1 / (x * x));
108 x += PI / 4.0;
109 val += (*kernel = sinx3 / (x * x));
110 val = 1.0 / val;
111 *(kernel--) *= val;
112 *(kernel--) *= val;
113 *(kernel--) *= val;
114 *(kernel--) *= val;
115 *(kernel--) *= val;
116 *(kernel--) *= val;
117 *(kernel--) *= val;
118 *kernel *= val;
119 }
120}
121
122inline void make_kernel(float pos, float *kernel, interpenum interptype) {
123 const float threshold = 1e-6;
124
125 switch (interptype) {
127 *kernel = 1.0;
128 break;
129 case INTERP_BILINEAR:
130 *(kernel++) = 1.0 - pos;
131 *kernel = pos;
132 break;
133 case INTERP_LANCZOS2:
134 makeLanczos2Kernel(pos, kernel, threshold);
135 break;
136 case INTERP_LANCZOS3:
137 makeLanczos3Kernel(pos, kernel, threshold);
138 break;
139 case INTERP_LANCZOS4:
140 makeLanczos4Kernel(pos, kernel, threshold);
141 break;
142
143 }
144}
145
146
147float interpolate_pix(float *pix, float x, float y,
148 int xsize, int ysize, interpenum interptype) {
149
150 static const int interp_kernwidth[5] = {1, 2, 4, 6, 8};
151
152 float buffer[INTERP_MAXKERNELWIDTH],
153 kernel[INTERP_MAXKERNELWIDTH],
154 *kvector, *pixin, *pixout,
155 dx, dy, val;
156 int i, j, ix, iy, kwidth, step;
157
158 kwidth = interp_kernwidth[interptype];
159
160 //-- Get the integer part of the current coordinate or nearest neighbour
161 if (interptype == INTERP_NEARESTNEIGHBOUR) {
162 ix = (int) (x - 0.50001);
163 iy = (int) (y - 0.50001);
164 }
165 else {
166 ix = (int) x;
167 iy = (int) y;
168 }
169
170 //-- Store the fractional part of the current coordinate
171 dx = x - ix;
172 dy = y - iy;
173 //-- Check if interpolation start/end exceed image boundary
174 ix -= kwidth / 2;
175 iy -= kwidth / 2;
176 if (ix < 0 || ix + kwidth <= 0 || ix + kwidth > xsize ||
177 iy < 0 || iy + kwidth <= 0 || iy + kwidth > ysize) {
178 return 0.0;
179 }
180
181 //-- First step: interpolate along NAXIS1 from the data themselves
182 make_kernel(dx, kernel, interptype);
183 step = xsize - kwidth;
184 pixin = pix + iy * xsize + ix; // Set starting pointer
185 pixout = buffer;
186 for (j = kwidth; j--;) {
187 val = 0.0;
188 kvector = kernel;
189 for (i = kwidth; i--;) {
190 val += *(kvector++) * *(pixin++);
191 }
192 *(pixout++) = val;
193 pixin += step;
194 }
195
196 //-- Second step: interpolate along NAXIS2 from the interpolation buffer
197 make_kernel(dy, kernel, interptype);
198 pixin = buffer;
199 val = 0.0;
200 kvector = kernel;
201 for (i = kwidth; i--;) {
202 val += *(kvector++) * *(pixin++);
203 }
204
205 return val;
206}
207
208
209inline double getClamped(const ImageInterfaceTypePtr& image, int x, int y) {
210 return Traits::at(image, std::max(0, std::min(x, (int) Traits::width(image) - 1)),
211 std::max(0, std::min(y, (int) Traits::height(image) - 1)));
212}
213
215 double scale_factor, double x_shift, double y_shift) {
216 int window_width = Traits::width(window);
217 int window_height = Traits::height(window);
218 for (int x_win = 0; x_win < window_width; x_win++) {
219 for (int y_win = 0; y_win < window_height; y_win++) {
220 double x = (x_win + 0.5 - x_shift) / scale_factor - 0.5;
221 double y = (y_win + 0.5 - y_shift) / scale_factor - 0.5;
222
223 int xi = std::floor(x);
224 int yi = std::floor(y);
225
226 double x_delta = x - xi;
227 double y_delta = y - yi;
228
229 double v00 = getClamped(source, xi, yi);
230 double v01 = getClamped(source, xi, yi + 1);
231 double v10 = getClamped(source, xi + 1, yi);
232 double v11 = getClamped(source, xi + 1, yi + 1);
233
234 Traits::at(window, x_win, y_win) = (1.0 - y_delta) * ((1.0 - x_delta) * v00 + x_delta * v10) +
235 y_delta * ((1.0 - x_delta) * v01 + x_delta * v11);
236 }
237 }
238}
239
241 double scale_factor, double x_shift, double y_shift) {
242 int window_width = Traits::width(window);
243 int window_height = Traits::height(window);
244
245 auto data = &source->getData()[0];
246 auto source_width = source->getWidth();
247 auto source_height = source->getHeight();
248
249 for (int x_win = 0; x_win < window_width; x_win++) {
250 float x = (x_win + 0.5 - x_shift) / scale_factor + 0.5;
251 for (int y_win = 0; y_win < window_height; y_win++) {
252 float y = (y_win + 0.5 - y_shift) / scale_factor + 0.5;
253 Traits::at(window, x_win, y_win) = interpolate_pix(data, x, y, source_width, source_height,
255 }
256 }
257}
258
260 double scale_factor, double x_shift, double y_shift) {
261 int window_width = Traits::width(window);
262 int window_height = Traits::height(window);
263
264 //auto data = &source->getData()[0];
265 auto source_width = source->getWidth();
266 auto source_height = source->getHeight();
267
268 //
269 float kernel[8];
270
271 // First resize vertically and store the result in a transposed buffer
272 auto buffer = Traits::factory(window_height, source_width);
273 for (unsigned int buff_x = 0; buff_x < Traits::width(buffer); buff_x++) {
274 float pos = (buff_x + 0.5 - y_shift) / scale_factor + 0.5;
275 int ipos = int(pos) - 4;
276
277 if (ipos < 0 || ipos + 7 >= source_height) {
278 continue;
279 }
280
281 make_kernel(pos - int(pos), kernel, INTERP_LANCZOS4);
282 for (unsigned int buff_y = 0; buff_y < Traits::height(buffer); buff_y++) {
283 float val = 0.f;
284 for (int i = 0; i < 8; i++) {
285 val += kernel[i] * Traits::at(source, buff_y, ipos + i);
286 }
287 Traits::at(buffer, buff_x, buff_y) = val;
288 }
289 }
290
291 // resize on the other axis
292 for (int x_win = 0; x_win < window_width; x_win++) {
293 float pos = (x_win + 0.5 - x_shift) / scale_factor + 0.5;
294 int ipos = int(pos) - 4;
295 if (ipos < 0 || ipos + 7 >= source_width) {
296 continue;
297 }
298 make_kernel(pos - int(pos), kernel, INTERP_LANCZOS4);
299
300 for (int y_win = 0; y_win < window_height; y_win++) {
301 float val = 0.f;
302 for (int i = 0; i < 8; i++) {
303 val += kernel[i] * Traits::at(buffer, y_win, ipos + i);
304 }
305 Traits::at(window, x_win, y_win) = val;
306 }
307 }
308}
309
310
311}
312
313namespace ModelFitting {
314
315// Adds the source_image to the target image scaled by scale_factor and centered at x, y
317 ImageInterfaceTypePtr& target_image, const ImageInterfaceTypePtr& source_image,
318 double scale_factor, double x, double y) {
319 // Calculate the size in pixels of the image2 after in the scale of image1
320 double scaled_width = width(source_image) * scale_factor;
321 double scaled_height = height(source_image) * scale_factor;
322 // Calculate the window of the image1 which is affected
323 int x_min = std::floor(x - scaled_width / 2.);
324 int x_max = std::ceil(x + scaled_width / 2.);
325 int window_width = x_max - x_min;
326 int y_min = std::floor(y - scaled_height / 2.);
327 int y_max = std::ceil(y + scaled_height / 2.);
328 int window_height = y_max - y_min;
329 // Calculate the shift of the image2 inside the window
330 double x_shift = x - scaled_width / 2. - x_min;
331 double y_shift = y - scaled_height / 2. - y_min;
332 // Create the scaled and shifted window
333 auto window = factory(window_width, window_height);
334
335 shiftResizeLancszosFast(source_image, window, scale_factor, x_shift, y_shift);
336
337 // We need to correct the window for the scaling, so it has the same integral
338 // with the image2
339 double corr_factor = 1. / (scale_factor * scale_factor);
340 // Add the window to the image1
341 for (int x_im = std::max(x_min, 0); x_im < std::min<int>(x_max, width(target_image)); ++x_im) {
342 for (int y_im = std::max(y_min, 0); y_im < std::min<int>(y_max, height(target_image)); ++y_im) {
343 int x_win = x_im - x_min;
344 int y_win = y_im - y_min;
345 at(target_image, x_im, y_im) += corr_factor * at(window, x_win, y_win);
346 }
347 }
348}
349
350}
351
#define INTERP_MAXKERNELWIDTH
T ceil(T... args)
int getWidth() const final
Returns the width of the image in pixels.
const std::vector< T > & getData() const
int getHeight() const final
Returns the height of the image in pixels.
T floor(T... args)
T max(T... args)
T min(T... args)
std::shared_ptr< ImageInterfaceType > ImageInterfaceTypePtr
float interpolate_pix(float *pix, float x, float y, int xsize, int ysize, interpenum interptype)
void shiftResize(const ImageInterfaceTypePtr &source, ImageInterfaceTypePtr &window, double scale_factor, double x_shift, double y_shift)
void shiftResizeLancszos(const ImageInterfaceTypePtr &source, ImageInterfaceTypePtr &window, double scale_factor, double x_shift, double y_shift)
static void makeLanczos4Kernel(float pos, float *kernel, const float threshold)
double getClamped(const ImageInterfaceTypePtr &image, int x, int y)
static void makeLanczos3Kernel(float pos, float *kernel, const float threshold)
void make_kernel(float pos, float *kernel, interpenum interptype)
void shiftResizeLancszosFast(const ImageInterfaceTypePtr &source, ImageInterfaceTypePtr &window, double scale_factor, double x_shift, double y_shift)
ModelFitting::ImageTraits< ImageInterfaceTypePtr > Traits
static void makeLanczos2Kernel(float pos, float *kernel, const float threshold)
static std::size_t height(const ImageInterfaceTypePtr &image)
static ImageInterfaceType::PixelType & at(ImageInterfaceTypePtr &image, std::size_t x, std::size_t y)
static std::size_t width(const ImageInterfaceTypePtr &image)
static ImageInterfaceTypePtr factory(std::size_t width, std::size_t height)
static void addImageToImage(ImageType &image1, const ImageType &image2, double scale, double x, double y)