1 /*
2 * * Copyright (C) 2006-2011 Anders Brander <anders@brander.dk>,
3 * * Anders Kvist <akv@lnxbx.dk> and Klaus Post <klauspost@gmail.com>
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 /* Plugin tmpl version 4 */
21
22 #include <rawstudio.h>
23 #include <string.h>
24
25 #define RS_TYPE_DEMOSAIC (rs_demosaic_type)
26 #define RS_DEMOSAIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), RS_TYPE_DEMOSAIC, RSDemosaic))
27 #define RS_DEMOSAIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), RS_TYPE_DEMOSAIC, RSDemosaicClass))
28 #define RS_IS_DEMOSAIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), RS_TYPE_DEMOSAIC))
29
30 typedef struct {
31 gint start_y;
32 gint end_y;
33 RS_IMAGE16 *image;
34 RS_IMAGE16 *output;
35 guint filters;
36 GThread *threadid;
37 } ThreadInfo;
38
39 typedef enum {
40 RS_DEMOSAIC_NONE,
41 RS_DEMOSAIC_BILINEAR,
42 RS_DEMOSAIC_PPG,
43 RS_DEMOSAIC_MAX,
44 RS_DEMOSAIC_NONE_HALF
45 } RS_DEMOSAIC;
46
47 const static gchar *rs_demosaic_ascii[RS_DEMOSAIC_MAX] = {
48 "none",
49 "bilinear",
50 "pixel-grouping"
51 };
52
53 typedef struct _RSDemosaic RSDemosaic;
54 typedef struct _RSDemosaicClass RSDemosaicClass;
55
56 struct _RSDemosaic {
57 RSFilter parent;
58
59 RS_DEMOSAIC method;
60 gboolean allow_half;
61 };
62
63 struct _RSDemosaicClass {
64 RSFilterClass parent_class;
65 };
66
67 RS_DEFINE_FILTER(rs_demosaic, RSDemosaic)
68
69 enum {
70 PROP_0,
71 PROP_METHOD,
72 PROP_ALLOW_HALF,
73 };
74
75 static void get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
76 static void set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
77 static RSFilterResponse *get_image(RSFilter *filter, const RSFilterRequest *request);
78 static inline int fc_INDI (const unsigned int filters, const int row, const int col);
79 static void border_interpolate_INDI (const ThreadInfo* t, int colors, int border);
80 static void lin_interpolate_INDI(RS_IMAGE16 *image, RS_IMAGE16 *output, const unsigned int filters, const int colors);
81 static void ppg_interpolate_INDI(RS_IMAGE16 *image, RS_IMAGE16 *output, const unsigned int filters, const int colors);
82 static void none_interpolate_INDI(RS_IMAGE16 *in, RS_IMAGE16 *out, const unsigned int filters, const int colors, gboolean half_size);
83 static void hotpixel_detect(const ThreadInfo* t);
84 static void expand_cfa_data(const ThreadInfo* t);
85
86
87 static RSFilterClass *rs_demosaic_parent_class = NULL;
88
89 G_MODULE_EXPORT void
rs_plugin_load(RSPlugin * plugin)90 rs_plugin_load(RSPlugin *plugin)
91 {
92 rs_demosaic_get_type(G_TYPE_MODULE(plugin));
93 }
94
95 static void
rs_demosaic_class_init(RSDemosaicClass * klass)96 rs_demosaic_class_init(RSDemosaicClass *klass)
97 {
98 RSFilterClass *filter_class = RS_FILTER_CLASS (klass);
99 GObjectClass *object_class = G_OBJECT_CLASS(klass);
100
101 rs_demosaic_parent_class = g_type_class_peek_parent (klass);
102
103 object_class->get_property = get_property;
104 object_class->set_property = set_property;
105
106 g_object_class_install_property(object_class,
107 PROP_METHOD, g_param_spec_string(
108 "method", "demosaic method", "The demosaic algorithm to use (\"bilinear\" or \"pixel-grouping\")",
109 rs_demosaic_ascii[RS_DEMOSAIC_PPG], G_PARAM_READWRITE)
110 );
111
112 g_object_class_install_property(object_class,
113 PROP_ALLOW_HALF, g_param_spec_boolean(
114 "demosaic-allow-downscale", "demosaic-allow-downscale", "Allow demosaic to return half size image",
115 FALSE, G_PARAM_READWRITE)
116 );
117
118 filter_class->name = "Demosaic filter";
119 filter_class->get_image = get_image;
120 }
121
122 static void
rs_demosaic_init(RSDemosaic * demosaic)123 rs_demosaic_init(RSDemosaic *demosaic)
124 {
125 demosaic->method = RS_DEMOSAIC_PPG;
126 }
127
128 static void
get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)129 get_property(GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
130 {
131 RSDemosaic *demosaic = RS_DEMOSAIC(object);
132
133 switch (property_id)
134 {
135 case PROP_METHOD:
136 g_value_set_string(value, rs_demosaic_ascii[demosaic->method]);
137 break;
138 case PROP_ALLOW_HALF:
139 g_value_set_boolean(value, demosaic->allow_half);
140 break;
141 default:
142 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
143 }
144 }
145
146 static void
set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)147 set_property(GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
148 {
149 gint i;
150 const gchar *str;
151 RSDemosaic *demosaic = RS_DEMOSAIC(object);
152
153 switch (property_id)
154 {
155 case PROP_METHOD:
156 str = g_value_get_string(value);
157 for(i=0;i<RS_DEMOSAIC_MAX;i++)
158 {
159 if (g_str_equal(rs_demosaic_ascii[i], str))
160 demosaic->method = i;
161 }
162 break;
163 case PROP_ALLOW_HALF:
164 demosaic->allow_half = g_value_get_boolean(value);
165 break;
166 default:
167 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
168 }
169 }
170
171 /*
172 In order to inline this calculation, I make the risky
173 assumption that all filter patterns can be described
174 by a repeating pattern of eight rows and two columns
175
176 Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2
177 */
178 #define FC(row,col) \
179 (int)(filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3)
180
181 static RSFilterResponse *
get_image(RSFilter * filter,const RSFilterRequest * request)182 get_image(RSFilter *filter, const RSFilterRequest *request)
183 {
184 RSDemosaic *demosaic = RS_DEMOSAIC(filter);
185 RSFilterResponse *previous_response;
186 RSFilterResponse *response;
187 RS_IMAGE16 *input;
188 RS_IMAGE16 *output = NULL;
189 guint filters;
190 RS_DEMOSAIC method;
191
192 previous_response = rs_filter_get_image(filter->previous, request);
193
194 input = rs_filter_response_get_image(previous_response);
195
196 if (!RS_IS_IMAGE16(input))
197 return previous_response;
198
199 /* Just pass on output from previous filter if the image is not CFA */
200 if (input->filters == 0)
201 {
202 g_object_unref(input);
203 return previous_response;
204 }
205
206 g_assert(input->channels == 1);
207 g_assert(input->filters != 0);
208
209 response = rs_filter_response_clone(previous_response);
210 g_object_unref(previous_response);
211
212 gint fuji_width;
213 if (rs_filter_param_get_integer(RS_FILTER_PARAM(response), "fuji-width", &fuji_width) && (fuji_width > 0))
214 demosaic->allow_half = FALSE;
215
216 method = demosaic->method;
217 if (rs_filter_request_get_quick(request))
218 {
219 method = RS_DEMOSAIC_NONE;
220 rs_filter_response_set_quick(response);
221 }
222
223 /* Magic - Ask Dave ;) */
224 filters = input->filters;
225 filters &= ~((filters & 0x55555555) << 1);
226
227 /* Check if pattern is 2x2, otherwise we cannot do "none" demosaic */
228 if (method == RS_DEMOSAIC_NONE)
229 if (! ( (filters & 0xff ) == ((filters >> 8) & 0xff) &&
230 ((filters >> 16) & 0xff) == ((filters >> 24) & 0xff) &&
231 (filters & 0xff) == ((filters >> 24) &0xff)))
232 method = RS_DEMOSAIC_PPG;
233
234 if (method == RS_DEMOSAIC_NONE)
235 {
236 if (demosaic->allow_half)
237 {
238 output = rs_image16_new(input->w/2, input->h/2, 3, 4);
239 rs_filter_param_set_boolean(RS_FILTER_PARAM(response), "half-size", TRUE);
240 method = RS_DEMOSAIC_NONE_HALF;
241 }
242 else
243 {
244 output = rs_image16_new(input->w, input->h, 3, 4);
245 }
246 }
247 else
248 output = rs_image16_new(input->w, input->h, 3, 4);
249
250 rs_filter_response_set_image(response, output);
251 g_object_unref(output);
252
253
254 switch (method)
255 {
256 case RS_DEMOSAIC_BILINEAR:
257 lin_interpolate_INDI(input, output, filters, 3);
258 break;
259 case RS_DEMOSAIC_PPG:
260 ppg_interpolate_INDI(input,output, filters, 3);
261 break;
262 case RS_DEMOSAIC_NONE:
263 none_interpolate_INDI(input, output, filters, 3, FALSE);
264 break;
265 case RS_DEMOSAIC_NONE_HALF:
266 none_interpolate_INDI(input, output, filters, 3, TRUE);
267 break;
268 default:
269 /* Do nothing */
270 break;
271 }
272
273 g_object_unref(input);
274 return response;
275 }
276
277 /*
278 The rest of this file is pretty much copied verbatim from dcraw/ufraw
279 */
280
281 #define FORCC for (c=0; c < colors; c++)
282
283
284 #define BAYER(row,col) \
285 image[((row) >> shrink)*iwidth + ((col) >> shrink)][FC(row,col)]
286
287 static inline int
fc_INDI(const unsigned int filters,const int row,const int col)288 fc_INDI (const unsigned int filters, const int row, const int col)
289 {
290 static const char filter[16][16] =
291 { { 2,1,1,3,2,3,2,0,3,2,3,0,1,2,1,0 },
292 { 0,3,0,2,0,1,3,1,0,1,1,2,0,3,3,2 },
293 { 2,3,3,2,3,1,1,3,3,1,2,1,2,0,0,3 },
294 { 0,1,0,1,0,2,0,2,2,0,3,0,1,3,2,1 },
295 { 3,1,1,2,0,1,0,2,1,3,1,3,0,1,3,0 },
296 { 2,0,0,3,3,2,3,1,2,0,2,0,3,2,2,1 },
297 { 2,3,3,1,2,1,2,1,2,1,1,2,3,0,0,1 },
298 { 1,0,0,2,3,0,0,3,0,3,0,3,2,1,2,3 },
299 { 2,3,3,1,1,2,1,0,3,2,3,0,2,3,1,3 },
300 { 1,0,2,0,3,0,3,2,0,1,1,2,0,1,0,2 },
301 { 0,1,1,3,3,2,2,1,1,3,3,0,2,1,3,2 },
302 { 2,3,2,0,0,1,3,0,2,0,1,2,3,0,1,0 },
303 { 1,3,1,2,3,2,3,2,0,2,0,1,1,0,3,0 },
304 { 0,2,0,3,1,0,0,1,1,3,3,2,3,2,2,1 },
305 { 2,1,3,2,3,1,2,1,0,3,0,2,0,2,0,2 },
306 { 0,3,1,0,0,2,0,3,2,1,3,1,1,3,1,3 } };
307
308 if (filters != 1) return FC(row,col);
309 /* Assume that we are handling the Leaf CatchLight with
310 * top_margin = 8; left_margin = 18; */
311 // return filter[(row+top_margin) & 15][(col+left_margin) & 15];
312 return filter[(row+8) & 15][(col+18) & 15];
313 }
314
315
316 static void
border_interpolate_INDI(const ThreadInfo * t,int colors,int border)317 border_interpolate_INDI (const ThreadInfo* t, int colors, int border)
318 {
319 int row, col, y, x, f, c, sum[8];
320 RS_IMAGE16* image = t->output;
321 guint filters = t->filters;
322
323 for (row=t->start_y; row < t->end_y; row++)
324 for (col=0; col < image->w; col++)
325 {
326 if (col==border && row >= border && row < image->h-border)
327 col = image->w-border;
328 memset (sum, 0, sizeof sum);
329 for (y=row-1; y != row+2; y++)
330 for (x=col-1; x != col+2; x++)
331 if (y >= 0 && y < image->h && x >= 0 && x < image->w)
332 {
333 f = FC(y, x);
334 sum[f] += GET_PIXEL(image, x, y)[f];
335 sum[f+4]++;
336 }
337 f = FC(row,col);
338 for (c=0; c < colors; c++)
339 if (c != f && sum[c+4])
340 image->pixels[row*image->rowstride+col*4+c] = sum[c] / sum[c+4];
341 }
342 }
343
344 static void
lin_interpolate_INDI(RS_IMAGE16 * input,RS_IMAGE16 * output,const unsigned int filters,const int colors)345 lin_interpolate_INDI(RS_IMAGE16 *input, RS_IMAGE16 *output, const unsigned int filters, const int colors) /*UF*/
346 {
347 ThreadInfo *t = g_new(ThreadInfo, 1);
348 t->image = input;
349 t->output = output;
350 t->filters = filters;
351 t->start_y = 0;
352 t->end_y = input->w;
353
354 expand_cfa_data(t);
355 RS_IMAGE16* image = output;
356
357 int code[16][16][32], *ip, sum[4];
358 int c, i, x, y, row, col, shift, color;
359 gushort *pix;
360
361 border_interpolate_INDI(t, colors, 1);
362 for (row=0; row < 16; row++)
363 for (col=0; col < 16; col++) {
364 ip = code[row][col];
365 memset (sum, 0, sizeof sum);
366 for (y=-1; y <= 1; y++)
367 for (x=-1; x <= 1; x++) {
368 shift = (y==0) + (x==0);
369 if (shift == 2) continue;
370 color = fc_INDI(filters,row+y,col+x);
371 *ip++ = (image->pitch*y + x)*4 + color;
372 *ip++ = shift;
373 *ip++ = color;
374 sum[color] += 1 << shift;
375 }
376 FORCC
377 if (c != fc_INDI(filters,row,col)) {
378 *ip++ = c;
379 *ip++ = 256 / sum[c];
380 }
381 }
382 for (row=1; row < image->h-1; row++)
383 for (col=1; col < image->w-1; col++) {
384 pix = GET_PIXEL(image, col, row);
385 ip = code[row & 15][col & 15];
386 memset (sum, 0, sizeof sum);
387 for (i=8; i--; ip+=3)
388 sum[ip[2]] += pix[ip[0]] << ip[1];
389 for (i=colors; --i; ip+=2)
390 pix[ip[0]] = sum[ip[0]] * ip[1] >> 8;
391 }
392 }
393
394 static void
expand_cfa_data(const ThreadInfo * t)395 expand_cfa_data(const ThreadInfo* t) {
396
397 RS_IMAGE16* input = t->image;
398 RS_IMAGE16* output = t->output;
399 guint filters = t->filters;
400 guint col, row;
401
402 /* Populate new image with bayer data */
403 for(row=t->start_y; row<t->end_y; row++)
404 {
405 gushort* src = GET_PIXEL(input, 0, row);
406 gushort* dest = GET_PIXEL(output, 0, row);
407 for(col=0;col<output->w;col++)
408 {
409 dest[fc_INDI(filters, row, col)] = *src;
410 dest += output->pixelsize;
411 src++;
412 }
413 }
414 }
415
416 /*
417 Patterned Pixel Grouping Interpolation by Alain Desbiolles
418 */
clampbits16(gint x)419 inline guint clampbits16(gint x) { guint32 _y_temp; if( (_y_temp=x>>16) ) x = ~_y_temp >> 16; return x;}
420
421 #define CLIP(x) clampbits16(x)
422 #define ULIM(x,y,z) ((y) < (z) ? CLAMP(x,y,z) : CLAMP(x,z,y))
423
424 static void
interpolate_INDI_part(ThreadInfo * t)425 interpolate_INDI_part(ThreadInfo *t)
426 {
427 RS_IMAGE16 *image = t->output;
428 const unsigned int filters = t->filters;
429
430 /* Subtract 3 from top and bottom */
431 const int start_y = MAX(3, t->start_y);
432 const int end_y = MIN(image->h-3, t->end_y);
433 int row, col, c, d;
434 int diffA, diffB, guessA, guessB;
435 int p = image->pitch;
436 int p3 = p*3;
437 gushort (*pix)[4];
438
439 {
440 /* Fill in the green layer with gradients and pattern recognition: */
441 for (row=start_y; row < end_y; row++)
442 for (col=3+(FC(row,3) & 1), c=FC(row,col); col < image->w-3; col+=2) {
443 pix = (gushort (*)[4])GET_PIXEL(image, col, row);
444
445 guessA = (pix[-1][1] + pix[0][c] + pix[1][1]) * 2
446 - pix[-2*1][c] - pix[2*1][c];
447 diffA = ( ABS(pix[-2*1][c] - pix[ 0][c]) +
448 ABS(pix[ 2*1][c] - pix[ 0][c]) +
449 ABS(pix[ -1][1] - pix[ 1][1]) ) * 3 +
450 ( ABS(pix[ 3*1][1] - pix[ 1][1]) +
451 ABS(pix[-3*1][1] - pix[-1][1]) ) * 2;
452
453 guessB = (pix[-p][1] + pix[0][c] + pix[p][1]) * 2
454 - pix[-2*p][c] - pix[2*p][c];
455 diffB = ( ABS(pix[-2*p][c] - pix[ 0][c]) +
456 ABS(pix[ 2*p][c] - pix[ 0][c]) +
457 ABS(pix[ -p][1] - pix[ p][1]) ) * 3 +
458 ( ABS(pix[ p3][1] - pix[ p][1]) +
459 ABS(pix[-p3][1] - pix[-p][1]) ) * 2;
460
461 if (diffA > diffB)
462 pix[0][1] = ULIM(guessB >> 2, pix[p][1], pix[-p][1]);
463 else
464 pix[0][1] = ULIM(guessA >> 2, pix[1][1], pix[-1][1]);
465 }
466 /* Calculate red and blue for each green pixel: */
467 for (row=start_y-2; row < end_y+2; row++)
468 for (col=1+(FC(row,2) & 1), c=FC(row,col+1); col < image->w-1; col+=2) {
469 pix = (gushort (*)[4])GET_PIXEL(image, col, row);
470 pix[0][c] = CLIP((pix[-1][c] + pix[1][c] + 2*pix[0][1]
471 - pix[-1][1] - pix[1][1]) >> 1);
472 c=2-c;
473 pix[0][c] = CLIP((pix[-p][c] + pix[p][c] + 2*pix[0][1]
474 - pix[-p][1] - pix[p][1]) >> 1);
475 c=2-c;
476 }
477
478 /* Calculate blue for red pixels and vice versa: */
479 for (row=start_y-2; row < end_y+2; row++)
480 for (col=1+(FC(row,1) & 1), c=2-FC(row,col); col < image->w-1; col+=2) {
481 pix = (gushort (*)[4])GET_PIXEL(image, col, row);
482 d = 1 + p;
483 diffA = ABS(pix[-d][c] - pix[d][c]) +
484 ABS(pix[-d][1] - pix[0][1]) +
485 ABS(pix[ d][1] - pix[0][1]);
486 guessA = pix[-d][c] + pix[d][c] + 2*pix[0][1]
487 - pix[-d][1] - pix[d][1];
488
489 d = p - 1;
490 diffB = ABS(pix[-d][c] - pix[d][c]) +
491 ABS(pix[-d][1] - pix[0][1]) +
492 ABS(pix[ d][1] - pix[0][1]);
493 guessB = pix[-d][c] + pix[d][c] + 2*pix[0][1]
494 - pix[-d][1] - pix[d][1];
495
496 if (diffA > diffB)
497 pix[0][c] = CLIP(guessB >> 1);
498 else
499 pix[0][c] = CLIP(guessA >> 1);
500 }
501 }
502 }
503
504 gpointer
start_interp_thread(gpointer _thread_info)505 start_interp_thread(gpointer _thread_info)
506 {
507 ThreadInfo* t = _thread_info;
508 hotpixel_detect(t);
509 expand_cfa_data(t);
510 border_interpolate_INDI (t, 3, 3);
511 interpolate_INDI_part(t);
512 g_thread_exit(NULL);
513
514 return NULL; /* Make the compiler shut up - we'll never return */
515 }
516
517 static void
ppg_interpolate_INDI(RS_IMAGE16 * image,RS_IMAGE16 * output,const unsigned int filters,const int colors)518 ppg_interpolate_INDI(RS_IMAGE16 *image, RS_IMAGE16 *output, const unsigned int filters, const int colors)
519 {
520 guint i, y_offset, y_per_thread, threaded_h;
521 const guint threads = rs_get_number_of_processor_cores();
522 ThreadInfo *t = g_new(ThreadInfo, threads);
523
524 threaded_h = image->h;
525 y_per_thread = (threaded_h + threads-1)/threads;
526 y_offset = 0;
527
528 for (i = 0; i < threads; i++)
529 {
530 t[i].image = image;
531 t[i].output = output;
532 t[i].filters = filters;
533 t[i].start_y = y_offset;
534 y_offset += y_per_thread;
535 y_offset = MIN(image->h, y_offset);
536 t[i].end_y = y_offset;
537
538 t[i].threadid = g_thread_create(start_interp_thread, &t[i], TRUE, NULL);
539 }
540
541 /* Wait for threads to finish */
542 for(i = 0; i < threads; i++)
543 g_thread_join(t[i].threadid);
544
545 g_free(t);
546 }
547
548
549 gpointer
start_none_thread(gpointer _thread_info)550 start_none_thread(gpointer _thread_info)
551 {
552 gint row, col;
553 gushort *src;
554 gushort *dest;
555
556 ThreadInfo* t = _thread_info;
557 gint ops = t->output->pixelsize;
558 gint ors = t->output->rowstride;
559 guint filters = t->filters;
560
561 for(row=t->start_y; row < t->end_y; row++)
562 {
563 src = GET_PIXEL(t->image, 0, row);
564 dest = GET_PIXEL(t->output, 0, row);
565 guint first = FC(row, 0);
566 guint second = FC(row, 1);
567 gint col_end = t->output->w & 0xfffe;
568
569 /* Green first or second?*/
570 if (first == 1) {
571 /* Green first, then red or blue */
572 /* Copy non-green to this and pixel below */
573 dest[second] = dest[second+ors] = src[1];
574 /* Copy green down */
575 dest[1+ors] = *src;
576 for(col=0 ; col < col_end; col += 2)
577 {
578 dest[1] = dest[1+ops]= *src;
579 /* Move to next pixel */
580 src++;
581 dest += ops;
582
583 dest[second] = dest[second+ops] =
584 dest[second+ors] = dest[second+ops+ors] = *src;
585
586 /* Move to next pixel */
587 dest += ops;
588 src++;
589 }
590 /* If uneven pixel width, copy last pixel */
591 if (t->output->w & 1) {
592 dest[0] = dest[-ops];
593 dest[1] = dest[-ops+1];
594 dest[2] = dest[-ops+2];
595 }
596 } else {
597 for(col=0 ; col < col_end; col += 2)
598 {
599 dest[first] = dest[first+ops] =
600 dest[first+ors] = dest[first+ops+ors] = *src;
601
602 dest += ops;
603 src++;
604
605 dest[1] = dest[1+ops]= *src;
606
607 dest += ops;
608 src++;
609 }
610 /* If uneven pixel width, copy last pixel */
611 if (t->output->w & 1) {
612 dest[0] = dest[-ops];
613 dest[1] = dest[-ops+1];
614 dest[2] = dest[-ops+2];
615 }
616 }
617 /* Duplicate first & last line */
618 if (t->end_y == t->output->h - 1)
619 {
620 memcpy(GET_PIXEL(t->output, 0, t->end_y), GET_PIXEL(t->output, 0, t->end_y - 1), t->output->rowstride * 2);
621 memcpy(GET_PIXEL(t->output, 0, 0), GET_PIXEL(t->output, 0, 1), t->output->rowstride * 2);
622 }
623 }
624 g_thread_exit(NULL);
625
626 return NULL; /* Make the compiler shut up - we'll never return */
627 }
628
629
630 gpointer
start_none_thread_half(gpointer _thread_info)631 start_none_thread_half(gpointer _thread_info)
632 {
633 gint row, col, i, j;
634 gushort *src;
635 gushort *dest;
636
637 ThreadInfo* t = _thread_info;
638 guint filters = t->filters;
639 gint col_end = t->output->w;
640
641 for(row=t->start_y; row < t->end_y; row++)
642 {
643 gint src_row = row*2;
644 src = GET_PIXEL(t->image, 0, src_row);
645 dest = GET_PIXEL(t->output, 0, row);
646 gushort *g_src = (FC(src_row, 0) == 1) ? &src[0] : &src[1];
647 gushort *r_src = NULL;
648 gushort *b_src = NULL;
649 for(i = src_row; i < src_row + 2; i++)
650 {
651 for( j = 0; j < 2; j ++)
652 {
653 if (FC(i, j) == 0)
654 r_src = GET_PIXEL(t->image, j, i);
655 if (FC(i, j) == 2)
656 b_src = GET_PIXEL(t->image, j, i);
657 }
658 }
659
660 g_assert(r_src);
661 g_assert(b_src);
662 for(col=0 ; col < col_end; col++)
663 {
664 *dest++ = *r_src;
665 *dest++ = *g_src;
666 *dest++ = *b_src;
667 dest++;
668 r_src+=2;
669 g_src+=2;
670 b_src+=2;
671 }
672
673 }
674 g_thread_exit(NULL);
675
676 return NULL; /* Make the compiler shut up - we'll never return */
677 }
678
679
680 static void
none_interpolate_INDI(RS_IMAGE16 * in,RS_IMAGE16 * out,const unsigned int filters,const int colors,gboolean half_size)681 none_interpolate_INDI(RS_IMAGE16 *in, RS_IMAGE16 *out, const unsigned int filters, const int colors, gboolean half_size)
682 {
683 guint i, y_offset, y_per_thread, threaded_h;
684 const guint threads = rs_get_number_of_processor_cores();
685 ThreadInfo *t = g_new(ThreadInfo, threads);
686
687 /* Subtract 1 from bottom */
688 threaded_h = out->h-1;
689 y_per_thread = (threaded_h + threads-1)/threads;
690 y_offset = 0;
691
692 for (i = 0; i < threads; i++)
693 {
694 t[i].image = in;
695 t[i].filters = filters;
696 t[i].start_y = y_offset;
697 t[i].output = out;
698 y_offset += y_per_thread;
699 y_offset = MIN(out->h-1, y_offset);
700 t[i].end_y = y_offset;
701
702 if (half_size)
703 t[i].threadid = g_thread_create(start_none_thread_half, &t[i], TRUE, NULL);
704 else
705 t[i].threadid = g_thread_create(start_none_thread, &t[i], TRUE, NULL);
706 }
707
708 /* Wait for threads to finish */
709 for(i = 0; i < threads; i++)
710 g_thread_join(t[i].threadid);
711
712 g_free(t);
713 }
714
715 static void
hotpixel_detect(const ThreadInfo * t)716 hotpixel_detect(const ThreadInfo* t)
717 {
718
719 RS_IMAGE16 *image = t->image;
720
721 gint x, y, end_y;
722 y = MAX( 4, t->start_y);
723 end_y = MIN(t->end_y, image->h - 4);
724
725 for(; y < end_y; y++)
726 {
727 gint col_end = image->w - 4;
728 gushort* img = GET_PIXEL(image, 0, y);
729 gint p = image->rowstride * 2;
730 gint p_one = image->rowstride;
731 for (x = 4; x < col_end ; x++) {
732 /* Calculate minimum difference to surrounding pixels */
733 gint left = (int)img[x - 2];
734 gint c = (int)img[x];
735 gint right = (int)img[x + 2];
736 gint up = (int)img[x - p];
737 gint down = (int)img[x + p];
738
739 gint d = ABS(c - left);
740 d = MIN(d, ABS(c - right));
741 d = MIN(d, ABS(c - up));
742 d = MIN(d, ABS(c - down));
743
744 /* Also calculate maximum difference between surrounding pixels themselves */
745 gint d2 = ABS(left - right);
746 d2 = MAX(d2, ABS(up - down));
747
748 /* If difference larger than surrounding pixels by a factor of 4,
749 replace with left/right pixel interpolation */
750
751 if ((d > d2 * 8) && (d > 2000)) {
752 /* Do extended test! */
753 left = (int)img[x - 4];
754 right = (int)img[x + 4];
755 up = (int)img[x - p * 2];
756 down = (int)img[x + p * 2];
757
758 d = MIN(d, ABS(c - left));
759 d = MIN(d, ABS(c - right));
760 d = MIN(d, ABS(c - up));
761 d = MIN(d, ABS(c - down));
762
763 /* Create threshold for surrounding pixels - also include other colors */
764 d2 = MAX(d2, ABS(left - right));
765 d2 = MAX(d2, ABS(up - down));
766 d = MIN(d, ABS(c - (int)img[x - 2 - p]));
767 d = MIN(d, ABS(c - (int)img[x + 2 - p]));
768 d = MIN(d, ABS(c - (int)img[x - 2 + p]));
769 d = MIN(d, ABS(c - (int)img[x + 2 + p]));
770 d2 = MAX(d2, ABS((int)img[x - 1] - (int)img[x + 1]));
771 d2 = MAX(d2, ABS((int)img[x - p_one] - (int)img[x + p_one]));
772 d2 = MAX(d2, ABS((int)img[x - 1 - p_one] - (int)img[x + 1 + p_one]));
773 d2 = MAX(d2, ABS((int)img[x - 1 + p_one] - (int)img[x + 1 - p_one]));
774 d2 = MAX(d2, ABS((int)img[x - 2 - p] - (int)img[x + 2 + p]));
775 d2 = MAX(d2, ABS((int)img[x - 2 + p] - (int)img[x + 2 - p]));
776
777 if ((d > d2 * 4) && (d > 1600)) {
778 img[x] = (gushort)(((gint)img[x-2] + (gint)img[x+2] + 1) >> 1);
779 }
780 }
781
782 }
783 }
784 }
785