This is post 0.16 material IMO. I've been working on a blurring
algorithm to eliminate noise (works, no UI yet) and discovered
that it is only possible to do well after white balance but before
interpolation/shrinking. The reason is that the channel noise levels
are modified by the WB multipliers. I think wavelet denoising should
have the same issue.
dcraw_finalize_interpolate() has now been split into two. A new function
named dcraw_finalize_raw() will do the initial processing up to and
including white balance for both shrink and interpolate code paths.
Wavelet denoising is applied afterwards. The shrink code does it still
in its own way with a scaled threshold but this can now be changed if
necessary (makes sense IMO).
Drawback is that all white balance adjusting need to redo the first
phase. So, I refactored a bit further to optimize the shrink code path
and optimized dcraw_finalize_raw() to be 6-7 times faster for the no-dark
frame case (on a dual core machine). The dark frame path can be optimized
too but I've left it as a reference for now. There's much more which
can be improved.
Zooming in to 50% and out to 33% have become noticeably faster
on my machine, an AMD X2 3800 dual core machine. Time spent in
ufraw_convert_image_first_phase() when changing the zoom level:
old new
33%->50% zoom 0.45s 0.15s (now using memcpy)
50%->33% zoom 0.35s 0.21s
This is how it currently looks:
diff --git a/dcraw_api.cc b/dcraw_api.cc
index 7ab18c0..8be8f9a 100644
--- a/dcraw_api.cc
+++ b/dcraw_api.cc
@@ -328,12 +328,96 @@ static int get_pixel(const dcraw_data *h, const
dcraw_data *dark,
return pixel;
}
+/*
+ * fc_INDI() optimizing wrapper.
+ * fc_sequence() cooks up the filter color sequence for a row knowing that
+ * it doesn't have to store more than 16 values. The result can be indexed
+ * by the column using fc_color() and that part must of course be inlined
+ * for maximum performance. The inner loop for image processing should
+ * always try to index the column and not the row in order to reduce the
+ * data cache footprint.
+ */
+static unsigned fc_sequence(int filters, int row)
+{
+ unsigned sequence = 0;
+ int c;
+
+ for (c = 15; c >= 0; --c)
+ sequence = (sequence << 2) | fc_INDI(filters, row, c);
+ return sequence;
+}
+
+/*
+ * Note: smart compilers will inline anyway in most cases: the "inline"
+ * below is a comment reminding not to make it an external function.
+ */
+static inline int fc_color(unsigned sequence, int col)
+{
+ return (sequence >> ((col << 1) & 0x1f)) & 3;
+}
+
+static inline void shrink_accumulate_row(unsigned *sum, int size,
+ dcraw_image_type *base, int scale, int color)
+{
+ int i, j;
+ unsigned v;
+
+ for (i = 0; i < size; ++i) {
+ v = 0;
+ for (j = 0; j < scale; ++j)
+ v += base[i * scale + j][color];
+ sum[i] += v;
+ }
+}
+
+static inline void shrink_row(dcraw_image_type *obase, int osize,
+ dcraw_image_type *ibase, int isize, int colors, int scale)
+{
+ unsigned sum[osize];
+ dcraw_image_type *iptr;
+ int cl, i;
+
+ for (cl = 0; cl < colors; ++cl) {
+ memset(sum, 0, sizeof (sum));
+ iptr = ibase;
+ for (i = 0; i < scale; ++i) {
+ shrink_accumulate_row(sum, osize, iptr, scale, cl);
+ iptr += isize;
+ }
+ for (i = 0; i < osize; ++i)
+ obase[i][cl] = sum[i] / (scale * scale);
+ }
+}
+
+static inline void shrink_pixel(dcraw_image_type pixp, int row, int col,
+ dcraw_data *hh, unsigned *fseq, int scale)
+{
+ unsigned sum[4], count[4];
+ int ri, ci, cl;
+ dcraw_image_type *ibase;
+
+ memset(sum, 0, sizeof (sum));
+ memset(count, 0, sizeof (count));
+ for (ri = 0; ri < scale; ++ri) {
+ ibase = hh->raw.image + ((row * scale + ri) / 2) * hh->raw.width;
+ for (ci = 0; ci < scale; ++ci) {
+ cl = fc_color(fseq[ri], col * scale + ci);
+ sum[cl] += ibase[(col * scale + ci) / 2][cl];
+ ++count[cl];
+ }
+ }
+ for (cl = 0; cl < hh->raw.colors; ++cl)
+ pixp[cl] = sum[cl] / count[cl];
+}
+
int dcraw_finalize_shrink(dcraw_image_data *f, dcraw_data *hh,
- dcraw_data *dark, int scale)
+ int scale)
{
DCRaw *d = (DCRaw *)hh->dcraw;
- int h, w, fujiWidth, r, c, ri, ci, cl, norm, s, recombine, pixels;
- int f4, sum[4], count[4];
+ int h, w, fujiWidth, r, c, ri, recombine, pixels, f4;
+ dcraw_image_type *ibase, *obase;
+ unsigned fseq[scale];
+ unsigned short *pixp;
g_free(d->messageBuffer);
d->messageBuffer = NULL;
@@ -341,56 +425,51 @@ int dcraw_finalize_shrink(dcraw_image_data *f, dcraw_data
*hh,
pixels = hh->raw.width * hh->raw.height;
recombine = ( hh->colors==3 && hh->raw.colors==4 );
+ /* the last row/column will be skipped if input is incomplete */
+ f->height = h = hh->height / scale;
+ f->width = w = hh->width / scale;
f->colors = hh->colors;
- int black = dark ? MAX(hh->black - dark->black, 0) : hh->black;
/* hh->raw.image is shrunk in half if there are filters.
* If scale is odd we need to "unshrink" it using the info in
* hh->fourColorFilters before scaling it. */
if (hh->filters!=0 && scale%2==1) {
- /* I'm skiping the last row/column if it is not a full row/column */
- f->height = h = hh->height / scale;
- f->width = w = hh->width / scale;
fujiWidth = hh->fuji_width / scale;
f->image = (dcraw_image_type *)
g_realloc(f->image, h * w * sizeof(dcraw_image_type));
f4 = hh->fourColorFilters;
- for(r=0; r<h; r++) {
- for(c=0; c<w; c++) {
- for (cl=0; cl<hh->raw.colors; cl++) sum[cl] = count[cl] = 0;
- for (ri=0; ri<scale; ri++)
- for (ci=0; ci<scale; ci++) {
- cl = fc_INDI(f4, r*scale+ri, c*scale+ci);
- sum[cl] += get_pixel(hh, dark,
- (r*scale+ri)/2, (c*scale+ci)/2, cl, pixels);
- count[cl]++;
- }
- for (cl=0; cl<hh->raw.colors; cl++)
- f->image[r*w+c][cl] =
- MAX(sum[cl]/count[cl] - black,0);
- if (recombine) f->image[r*w+c][1] =
- (f->image[r*w+c][1] + f->image[r*w+c][3])>>1;
+
+#ifdef _OPENMP
+ #pragma omp parallel for schedule(static,64) private(r,ri,fseq,c,pixp)
+#endif
+ for (r = 0; r < h; ++r) {
+ for (ri = 0; ri < scale; ++ri)
+ fseq[ri] = fc_sequence(f4, r + ri);
+ for (c = 0; c < w; ++c) {
+ pixp = f->image[r * w + c];
+ shrink_pixel(pixp, r, c, hh, fseq, scale);
+ if (recombine)
+ pixp[1] = (pixp[1] + pixp[3]) / 2;
}
}
} else {
- /* I'm skiping the last row/column if it is not a full row/column */
- f->height = h = hh->height / scale;
- f->width = w = hh->width / scale;
if (hh->filters!=0) scale /= 2;
fujiWidth = ( (hh->fuji_width+hh->shrink) >> hh->shrink ) / scale;
f->image = (dcraw_image_type *)g_realloc(
f->image, h * w * sizeof(dcraw_image_type));
- norm = scale * scale;
- for(r=0; r<h; r++) {
- for(c=0; c<w; c++) {
- for (cl=0; cl<hh->raw.colors; cl++) {
- for (ri=0, s=0; ri<scale; ri++)
- for (ci=0; ci<scale; ci++)
- s += get_pixel(hh, dark, r*scale+ri, c*scale+ci,
cl, pixels);
- f->image[r*w+c][cl] = MAX(s/norm - black,0);
- }
- if (recombine) f->image[r*w+c][1] =
- (f->image[r*w+c][1] + f->image[r*w+c][3])>>1;
+#ifdef _OPENMP
+ #pragma omp parallel for schedule(static,64) private(r,ibase,obase,c)
+#endif
+ for (r = 0; r < h; ++r) {
+ ibase = hh->raw.image + r * hh->raw.width * scale;
+ obase = f->image + r * w;
+ if (scale == 1)
+ memcpy(obase, ibase, sizeof (dcraw_image_type) * w);
+ else
+ shrink_row(obase, w, ibase, hh->raw.width, hh->raw.colors,
scale);
+ if (recombine) {
+ for (c = 0; c < w; c++)
+ obase[c][1] = (obase[c][1] + obase[c][3]) / 2;
}
}
}
@@ -529,8 +608,51 @@ void dcraw_wavelet_denoise_shrinked(dcraw_image_data *f,
float threshold)
NULL, threshold, 0);
}
+void dcraw_finalize_raw(dcraw_data *h, dcraw_data *dark, int rgbWB[4])
+{
+ int r, c, cc, pixels;
+ unsigned f4, px, fseq, black;
+ dcraw_image_type *base;
+
+ pixels = h->raw.width * h->raw.height;
+ black = dark ? MAX(h->black - dark->black, 0) : h->black;
+ f4 = h->fourColorFilters;
+ if (h->colors == 3)
+ rgbWB[3] = rgbWB[1];
+ if (dark) {
+ for(r=0; r<h->height; r++)
+ for(c=0; c<h->width; c++) {
+ int cc = fc_INDI(f4,r,c);
+ h->raw.image[r/2 * h->raw.width + c/2][cc] = MIN( MAX( (gint64)
+ (get_pixel(h, dark, r/2, c/2, cc, pixels) - black) *
+ rgbWB[cc]/0x10000, 0), 0xFFFF);
+ }
+ } else {
+#ifdef _OPENMP
+#pragma omp parallel for schedule(static,64) default(none) \
+ shared(h,dark,rgbWB,pixels,f4,black) \
+ private(r,base,fseq,c,cc,px)
+#endif
+ for (r = 0; r < h->height; r++) {
+ base = h->raw.image + (r/2) * h->raw.width;
+ fseq = fc_sequence(f4, r);
+ for (c = 0; c < h->width; c++) {
+ cc = fc_color(fseq, c);
+ if (base[c/2][cc] < black) {
+ base[c/2][cc] = 0;
+ continue;
+ }
+ px = (base[c/2][cc] - black) * (guint64)(unsigned)(rgbWB[cc]) /
0x10000;
+ if (px > 0xffff)
+ px = 0xffff;
+ base[c/2][cc] = px;
+ }
+ }
+ }
+}
+
int dcraw_finalize_interpolate(dcraw_image_data *f, dcraw_data *h,
- dcraw_data *dark, int interpolation, int smoothing, int rgbWB[4])
+ int interpolation, int smoothing)
{
DCRaw *d = (DCRaw *)h->dcraw;
int fujiWidth, i, r, c, cl, pixels;
@@ -560,7 +682,6 @@ int dcraw_finalize_interpolate(dcraw_image_data *f,
dcraw_data *h,
} else {
ff = h->filters &= ~((h->filters & 0x55555555) << 1);
}
- int black = dark ? MAX(h->black - dark->black, 0) : h->black;
/* It might be better to report an error here: */
/* (dcraw also forbids AHD for Fuji rotated images) */
@@ -569,13 +690,11 @@ int dcraw_finalize_interpolate(dcraw_image_data *f,
dcraw_data *h,
if ( interpolation==dcraw_ppg_interpolation && h->colors > 3 )
interpolation = dcraw_vng_interpolation;
f4 = h->fourColorFilters;
- if (h->colors==3) rgbWB[3] = rgbWB[1];
for(r=0; r<h->height; r++)
for(c=0; c<h->width; c++) {
int cc = fc_INDI(f4,r,c);
- f->image[r*f->width+c][fc_INDI(ff,r,c)] = MIN( MAX( (gint64)
- (get_pixel(h, dark, r/2, c/2, cc, pixels) - black) *
- rgbWB[cc]/0x10000, 0), 0xFFFF);
+ f->image[r*f->width+c][fc_INDI(ff,r,c)] =
+ h->raw.image[r/2 * h->raw.width + c/2][cc];
}
int smoothPasses = 1;
if (interpolation==dcraw_bilinear_interpolation)
diff --git a/dcraw_api.h b/dcraw_api.h
index 16a60d3..218ebf6 100644
--- a/dcraw_api.h
+++ b/dcraw_api.h
@@ -65,15 +65,16 @@ void dcraw_set_progress_handle(dcraw_data *h,
int dcraw_load_raw(dcraw_data *h);
int dcraw_load_thumb(dcraw_data *h, dcraw_image_data *thumb);
int dcraw_finalize_shrink(dcraw_image_data *f, dcraw_data *h,
- dcraw_data *dark, int scale);
+ int scale);
int dcraw_image_resize(dcraw_image_data *image, int size);
int dcraw_image_stretch(dcraw_image_data *image, double pixel_aspect);
int dcraw_flip_image(dcraw_image_data *image, int flip);
int dcraw_set_color_scale(dcraw_data *h, int useAutoWB, int useCameraWB);
void dcraw_wavelet_denoise(dcraw_data *h, float threshold);
void dcraw_wavelet_denoise_shrinked(dcraw_image_data *f, float threshold);
+void dcraw_finalize_raw(dcraw_data *h, dcraw_data *dark, int rgbWB[4]);
int dcraw_finalize_interpolate(dcraw_image_data *f, dcraw_data *h,
- dcraw_data *dark, int interpolation, int smoothing, int rgbWB[4]);
+ int interpolation, int smoothing);
void dcraw_close(dcraw_data *h);
int dcraw_image_dimensions(dcraw_data *raw, int flip, int *height, int *width);
diff --git a/dcraw_indi.c b/dcraw_indi.c
index 2c2e36f..3781abc 100644
--- a/dcraw_indi.c
+++ b/dcraw_indi.c
@@ -115,7 +115,7 @@ static void CLASS hat_transform (float *temp, float *base,
int st, int size, int
void CLASS wavelet_denoise_INDI(ushort (*image)[4], const int black,
const int iheight, const int iwidth, const int height, const int width,
const int colors, const int shrink, const float pre_mul[4],
- const float threshold, const unsigned filters)
+ float threshold, const unsigned filters)
{
float *fimg=0, /* *temp,*/ thold, mul[2], avg, diff;
int /*scale=1,*/ size, lev, hpass, lpass, row, col, nc, c, i, wlast;
@@ -139,10 +139,19 @@ void CLASS wavelet_denoise_INDI(ushort (*image)[4], const
int black,
float temp[iheight + iwidth];
#endif
if ((nc = colors) == 3 && filters) nc++;
+
+ // When denoising is done before WB then all samples are apparently assumed
+ // to be in the 0..4095 range. However, that range is camera specific and can
+ // vary. Doing WB before denoising solves that issue because it normalizes
+ // the pixel values to use all 16 bits, something the original code did
+ // using the "<< scale" code.
+ // The strange thing is that apparently it is now necessary to "compensate"
+ // for this fix by scaling the threshold.
+ threshold *= sqrt(0x10000 / 4096);
#ifdef _OPENMP
#pragma omp parallel for \
default(none) \
- shared(nc,image,size) \
+ shared(nc,image,size,threshold) \
private(c,i,hpass,lev,lpass,row,col,thold,fimg,temp)
#endif
FORC(nc) { /* denoise R,G1,B,G3 individually */
diff --git a/ufraw.h b/ufraw.h
index 914dfa4..fda74b3 100644
--- a/ufraw.h
+++ b/ufraw.h
@@ -94,6 +94,7 @@ typedef struct {
unsigned rgbMax, max, exposure, colors, useMatrix;
int restoreDetails, clipHighlights;
int doWB, rgbWB[4], colorMatrix[3][4];
+ int doDenoise, doInterpolate;
double gamma, linear;
char profileFile[profile_types][max_path];
void *profile[profile_types];
diff --git a/ufraw_developer.c b/ufraw_developer.c
index cb08f1a..76f79a5 100644
--- a/ufraw_developer.c
+++ b/ufraw_developer.c
@@ -42,6 +42,8 @@ developer_data *developer_init()
developer_data *d = g_new(developer_data,1);
d->mode = -1;
d->doWB = 1;
+ d->doDenoise = 1;
+ d->doInterpolate = 1;
d->gamma = -1;
d->linear = -1;
d->saturation = -1;
diff --git a/ufraw_preview.c b/ufraw_preview.c
index ddcaf7d..fcaf7cf 100644
--- a/ufraw_preview.c
+++ b/ufraw_preview.c
@@ -861,8 +861,7 @@ static void render_init(preview_data *data)
void preview_invalidate_layer(preview_data *data, UFRawPhase phase)
{
- if (phase==ufraw_denoise_phase && !Developer->doWB)
- // !doWB means denoise should be done in first phase
+ if (phase==ufraw_denoise_phase && !Developer->doDenoise)
phase = ufraw_first_phase;
for (; phase < ufraw_phases_num; phase++)
data->UF->Images[phase].valid = 0;
@@ -2807,7 +2806,7 @@ static void toggle_button_update(GtkToggleButton *button,
gboolean *valuep)
start_blink(data);
switch_highlights(data);
} else if ( valuep==&CFG->smoothing ) {
- if (!Developer->doWB) // !doWB means do interpolate
+ if (Developer->doInterpolate)
preview_invalidate_layer(data, ufraw_first_phase);
render_preview(data);
#ifdef UFRAW_HOTPIXELS
@@ -3236,8 +3235,7 @@ static void combo_update_simple(GtkWidget *combo,
UFRawPhase phase)
if (CFG->autoExposure==enabled_state) CFG->autoExposure = apply_state;
if (CFG->autoBlack==enabled_state) CFG->autoBlack = apply_state;
- if (phase==ufraw_first_phase && Developer->doWB)
- // doWB means don't interpolate
+ if (phase==ufraw_first_phase && !Developer->doInterpolate)
phase = ufraw_develop_phase;
preview_invalidate_layer(data, phase);
update_scales(data);
diff --git a/ufraw_ufraw.c b/ufraw_ufraw.c
index eb4bb8d..6f556b7 100644
--- a/ufraw_ufraw.c
+++ b/ufraw_ufraw.c
@@ -986,15 +986,18 @@ int ufraw_convert_image_first_phase(ufraw_data *uf,
gboolean lensfix)
ufraw_shave_hotpixels(uf, raw->raw.image, raw->raw.width, raw->raw.height,
raw->raw.colors, raw->rgbMax);
#endif
+ dcraw_finalize_raw(raw, dark, uf->developer->rgbWB);
+ uf->developer->doWB = 0;
if ( uf->ConvertShrink>1 || !uf->HaveFilters ) {
- dcraw_finalize_shrink(&final, raw, dark, uf->ConvertShrink);
- uf->developer->doWB = 1;
+ dcraw_finalize_shrink(&final, raw, uf->ConvertShrink);
+ uf->developer->doDenoise = 1;
+ uf->developer->doInterpolate = 0;
} else {
dcraw_wavelet_denoise(raw, uf->conf->threshold);
- dcraw_finalize_interpolate(&final, raw, dark,
- uf->conf->interpolation, uf->conf->smoothing,
- uf->developer->rgbWB);
- uf->developer->doWB = 0;
+ dcraw_finalize_interpolate(&final, raw,
+ uf->conf->interpolation, uf->conf->smoothing);
+ uf->developer->doDenoise = 0;
+ uf->developer->doInterpolate = 1;
}
g_free(raw->raw.image);
raw->raw.image = rawimage;
@@ -1063,8 +1066,7 @@ int ufraw_convert_image_first_phase(ufraw_data *uf,
gboolean lensfix)
static gboolean ufraw_do_denoise_phase(ufraw_data *uf)
{
- // !doWB means denoise was done in first phase
- return uf->conf->threshold>0 && uf->developer->doWB;
+ return uf->conf->threshold>0 && uf->developer->doDenoise;
}
int ufraw_convert_image_init_phase(ufraw_data *uf)
--
Frank
------------------------------------------------------------------------------
Come build with us! The BlackBerry(R) Developer Conference in SF, CA
is the only developer event you need to attend this year. Jumpstart your
developing skills, take BlackBerry mobile applications to market and stay
ahead of the curve. Join us from November 9 - 12, 2009. Register now!
http://p.sf.net/sfu/devconference
_______________________________________________
ufraw-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/ufraw-devel