/* WF debanding code Copyright 2011 by Yan Vladimirovich Used in LibRaw with author permission. LibRaw is free software; you can redistribute it and/or modify it under the terms of the one of two licenses as you choose: 1. GNU LESSER GENERAL PUBLIC LICENSE version 2.1 (See file LICENSE.LGPL provided in LibRaw distribution archive for details). 2. COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 (See file LICENSE.CDDL provided in LibRaw distribution archive for details). */ #define P1 imgdata.idata #define S imgdata.sizes #define O imgdata.params #define C imgdata.color #define T imgdata.thumbnail #define IO libraw_internal_data.internal_output_params #define ID libraw_internal_data.internal_data int LibRaw::wf_remove_banding() { #define WF_IMGMODE_BAYER4PLANE 4 #define WF_IMGMODE_BAYER1PLANE 1 #define WF_GREENMODE_IND 0 #define WF_GREENMODE_GX_XG 1 #define WF_GREENMODE_XG_GX 2 #define WF_DEBANDING_OK 0 #define WF_DEBANDING_NOTBAYER2X2 1 #define WF_DEBANDING_TOOSMALL 2 #define WF_GAUSS_PIRAMID_SIZE 4 #define WF_MAXTRESHOLD 65536 #define WF_BAYERSRC(row, col, c) ((ushort(*)[4])imgdata.image)[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][c] #define WF_BAYERGAU(l, row, col) (gauss_pyramid[l])[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)] #define WF_BAYERDFG(l, row, col) (difwg_pyramid[l])[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)] #define MIN(a,b) ((a) < (b) ? (a) : (b)) #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define WF_i_1TO4 for(int i=0; i<4; i++) // too small? if (S.width<128 || S.height<128) return WF_DEBANDING_TOOSMALL; // is 2x2 bayer? int bayer2x2flag=-1; for(int row_shift=0; row_shift<=8; row_shift+=2) { for(int col_shift=0; col_shift<=8; col_shift+=2) { if ((FC(0,0)!=FC(row_shift, col_shift)) || (FC(1,0)!=FC(row_shift+1, col_shift)) || (FC(0,1)!=FC(row_shift, col_shift+1)) || (FC(1,1)!=FC(row_shift+1, col_shift+1))) { bayer2x2flag=0; } } } if (bayer2x2flag==0) return WF_DEBANDING_NOTBAYER2X2; int x_green_flag = -1; int width_d2, height_d2; int width_p1_d2, height_p1_d2; width_d2 = S.width/2; height_d2 = S.height/2; width_p1_d2 = (S.width+1)/2; height_p1_d2 = (S.height+1)/2; ushort val_max_c[4]={0,0,0,0}; ushort val_max; ushort dummy_pixel=0; ushort *dummy_line; dummy_line = (ushort*)calloc(S.width, sizeof(ushort)*4); for(int i=0; isrc_first && src[0]= (1 << 8)) { val_max_s >>= 8; data_shift -= 8; } if (val_max_s >= (1 << 4)) { val_max_s >>= 4; data_shift -= 4; } if (val_max_s >= (1 << 2)) { val_max_s >>= 2; data_shift -= 2; } if (val_max_s >= (1 << 1)) { data_shift -= 1; } data_mult = 1<src_first && src[0]src_first && src[0]>1,i&1)]; val_accepted = val_max-3*MAX(MAX(treshold[0],treshold[1]),MAX(treshold[2],treshold[3])); float (*tr_weight)[4]; tr_weight=(float(*)[4])calloc(WF_MAXTRESHOLD*4, sizeof(float)); WF_i_1TO4 treshold[i]*=data_mult; for(int v=0; vsrc_first && src[0]0) { banding_col[row_d2][i]=banding_col[row_d2][i]/banding_col_count[row_d2][i]; bsum[i]+=banding_col[row_d2][i]; } } WF_i_1TO4 bmean[i]=bsum[i]/(i<2?height_d2:height_p1_d2); for(int row_d2=0; row_d20) { banding_row[col_d2][i]=(banding_row[col_d2][i]/banding_row_count[col_d2][i]); bsum[i]+=banding_row[col_d2][i]; } } WF_i_1TO4 bmean[i]=bsum[i]/(i<2?width_d2:width_p1_d2); for(int col_d2=0; col_d20) banding_row_i[col_d2][i]=int(banding_row[col_d2][i]-bmean[i]); for(int row_d2=0; row_d2=val_accepted) { val_new[i]=*src[i]>>data_shift; } else { if (val_new[i]>val_max) val_new[i]=val_max; else if (val_new[i]<0) val_new[i]=0; val_new[i]>>=data_shift; } } WF_i_1TO4 *src[i]=val_new[i]; // Next 4 pixel or exit if (src[0]src_first && src[0]r2) { rmax=r1; rmin=r2; rmax_greenmode=r1_greenmode; rmin_greenmode=r2_greenmode; } else { rmax=r2; rmin=r1; rmax_greenmode=r2_greenmode; rmin_greenmode=r1_greenmode; } int rmin_x2_p1, rmax_x2_p1; rmin_x2_p1=rmin*2+1; rmax_x2_p1=rmax*2+1; double gau_kernel_rmin[WF_MAXFILTERSIZE]; double gau_kernel_rmax[WF_MAXFILTERSIZE]; for(int i=0; i0; j--) gau_kernel_rmin[j]=0.5*(gau_kernel_rmin[j]+gau_kernel_rmin[j-1]); } for(int i=0; i<=rmax_x2_p1; i++) gau_kernel_rmax[i]=gau_kernel_rmin[i]; for(int i=rmin_x2_p1+1; i<=rmax_x2_p1; i++) { for(int j=i; j>0; j--) gau_kernel_rmax[j]=0.5*(gau_kernel_rmax[j]+gau_kernel_rmax[j-1]); } double wmin_sum, wmax_sum, energy_sum; wmin_sum=0; wmax_sum=0; energy_sum=0; for(int row=-rmax*2-1; row<=rmax*2+1; row++) { for(int col=-rmax*2-1; col<=rmax*2+1; col++) { double wght_rmax=0; double wght_rmin=0; #define WF_WMAX(row, col) (((abs(row)<=rmax*2)&&(abs(col)<=rmax*2))?gau_kernel_rmax[abs(row)/2+rmax+1]*gau_kernel_rmax[abs(col)/2+rmax+1]:0) #define WF_WMIN(row, col) (((abs(row)<=rmin*2)&&(abs(col)<=rmin*2))?gau_kernel_rmin[abs(row)/2+rmin+1]*gau_kernel_rmin[abs(col)/2+rmin+1]:0) if ( ((row&1)==0) && ((col&1)==0)) { wght_rmax = WF_WMAX(row, col); wght_rmin = WF_WMIN(row, col); } if (rmax_greenmode) { if ( ((row&1)==0) && ((col&1)==0)) wght_rmax = 0.5*wght_rmax; else if ( ((row&1)==1) && ((col&1)==1)) { wght_rmax = 0.125*(WF_WMAX(row-1, col-1)+WF_WMAX(row-1, col+1)+WF_WMAX(row+1, col-1)+WF_WMAX(row+1, col+1)); } } if (rmin_greenmode) { if ( ((row&1)==0) && ((col&1)==0)) wght_rmin = 0.5*wght_rmin; else if ( ((row&1)==1) && ((col&1)==1)) { wght_rmin = 0.125*(WF_WMIN(row-1, col-1)+WF_WMIN(row-1, col+1)+WF_WMIN(row+1, col-1)+WF_WMIN(row+1, col+1)); } } wmin_sum+=wght_rmin; wmax_sum+=wght_rmax; energy_sum+=(wght_rmax-wght_rmin)*(wght_rmax-wght_rmin); } } return energy_sum; } void LibRaw::wf_bayer4_green_blur(int mode, void* src_image, int src_imgmode, void* dst_image, int dst_imgmode) { /* This function filters green (or any "diagonal") channel of bayer4 data with "X" kernel, 1 1 4 1 1 */ #define WF_BAYERSRC4(row, col, c) ((ushort(*)[4])src_image)[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][c] #define WF_BAYERSRC1(row, col) ((ushort*)src_image) [((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)] #define WF_BAYERDST4(row, col, c) ((ushort(*)[4])dst_image)[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][c] #define WF_BAYERDST1(row, col) ((ushort*)dst_image) [((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)] int green_mode; if ( imgdata.idata.cdesc[FC(0, 0)] == imgdata.idata.cdesc[FC(1, 1)] ) green_mode = WF_GREENMODE_GX_XG; else if ( imgdata.idata.cdesc[FC(0, 1)] == imgdata.idata.cdesc[FC(1, 0)] ) green_mode = WF_GREENMODE_XG_GX; else green_mode = WF_GREENMODE_IND; int src_h_shift, dst_h_shift, src_h_shift_x2; if (src_imgmode == WF_IMGMODE_BAYER1PLANE) src_h_shift = 2 >> IO.shrink; else if (src_imgmode == WF_IMGMODE_BAYER4PLANE) src_h_shift = 8 >> IO.shrink; src_h_shift_x2 = src_h_shift*2; if (dst_imgmode == WF_IMGMODE_BAYER1PLANE) dst_h_shift = 2 >> IO.shrink; else if (dst_imgmode == WF_IMGMODE_BAYER4PLANE) dst_h_shift = 8 >> IO.shrink; int row, col; long int *line_filtered; line_filtered = (long int*) calloc(S.width, sizeof(*line_filtered)); ushort *src, *src_c, *src_u1, *src_u2, *src_d1, *src_d2, *dst_c, *src_ca, *dst_ca, *dst_rb; int start_col, start_col_left, row_up, row_dn; if ( green_mode != WF_GREENMODE_IND) { for(row=0; row0) { if ( green_mode == WF_GREENMODE_GX_XG ) start_col = ( row+1 ) & 1; else start_col = row & 1; switch (dst_imgmode) { case WF_IMGMODE_BAYER1PLANE: dst_c = &WF_BAYERDST1(row-1, start_col); break; case WF_IMGMODE_BAYER4PLANE: dst_c = &WF_BAYERDST4(row-1, start_col, FC(row-1, start_col)); break; } for (col=start_col; col>3; dst_c+=dst_h_shift; } if (src_image != dst_image) { // copy red or blue channel if ( green_mode == WF_GREENMODE_GX_XG ) start_col = row & 1; else start_col = (row+1) & 1; switch (src_imgmode) { case WF_IMGMODE_BAYER1PLANE: src = &WF_BAYERSRC1(row-1, start_col); break; case WF_IMGMODE_BAYER4PLANE: src = &WF_BAYERSRC4(row-1, start_col, FC(row-1, start_col)); break; } switch (dst_imgmode) { case WF_IMGMODE_BAYER1PLANE: dst_rb = &WF_BAYERDST1(row-1, start_col); break; case WF_IMGMODE_BAYER4PLANE: dst_rb = &WF_BAYERDST4(row-1, start_col, FC(row-1, start_col)); break; } for (col=start_col; col>3; dst_c+=dst_h_shift; } if (src_image != dst_image) { // copy red or blue channel if ( green_mode == WF_GREENMODE_GX_XG ) start_col = row & 1; else start_col = (row+1) & 1; switch (src_imgmode) { case WF_IMGMODE_BAYER1PLANE: src = &WF_BAYERSRC1(row-1, start_col); break; case WF_IMGMODE_BAYER4PLANE: src = &WF_BAYERSRC4(row-1, start_col, FC(row-1, start_col)); break; } switch (dst_imgmode) { case WF_IMGMODE_BAYER1PLANE: dst_rb = &WF_BAYERDST1(row-1, start_col); break; case WF_IMGMODE_BAYER4PLANE: dst_rb = &WF_BAYERDST4(row-1, start_col, FC(row-1, start_col)); break; } for (col=start_col; col> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][c] #define WF_BAYERSRC1(row, col) ((ushort*)src_image) [((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)] #define WF_BAYERDST4(row, col, c) ((ushort(*)[4])dst_image)[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][c] #define WF_BAYERDST1(row, col) ((ushort*)dst_image) [((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)] if (radius <= 0 || radius > 8) return; long int (*line_filtered)[4]; long int gauss_conv_kernel[9][4]; long int gauss_conv_kernel_c[8][9] = { {32768, 16384}, {24576, 16384, 4096}, {20480, 15360, 6144, 1024}, {17920, 14336, 7168, 2048, 256}, {16128, 13440, 7680, 2880, 640, 64}, {14784, 12672, 7920, 3520, 1056, 192, 16}, {13728, 12012, 8008, 4004, 1456, 364, 56, 4}, {12870, 11440, 8008, 4368, 1820, 560, 120, 16, 1}, }; int line_memory_len = (MAX(S.height, S.width)+1)/2+radius*2+1; line_filtered = (long int(*)[4]) calloc(line_memory_len, sizeof(long int[4])); int src_h_shift, src_v_shift; int dst_h_shift, dst_v_shift; if (src_imgmode == WF_IMGMODE_BAYER1PLANE) src_h_shift = 2 >> IO.shrink; else if (src_imgmode == WF_IMGMODE_BAYER4PLANE) src_h_shift = 8 >> IO.shrink; src_v_shift = S.width*src_h_shift; if (dst_imgmode == WF_IMGMODE_BAYER1PLANE) dst_h_shift = 2 >> IO.shrink; else if (dst_imgmode == WF_IMGMODE_BAYER4PLANE) dst_h_shift = 8 >> IO.shrink; dst_v_shift = S.width*dst_h_shift; int width_d2 = S.width / 2; int height_d2 = S.height / 2; int i, j; for (j=0; j<=radius; j++) { for (i=0; i<4; i++) { gauss_conv_kernel[j][i] = gauss_conv_kernel_c[radius-1][j]; } } int row, col; int rowf, colf; ushort *src [4], *dst[4]; long int src_c[4]; // Horizontal int right_edge[4]; for (i=0; i<4; i++) { int padding = i<2 && (S.width & 1) ? 1 : 0; right_edge[i]=width_d2 + radius + padding; } for(row=0; row>16; dst[i]+=dst_h_shift; } colf++; } if (col == S.width-1) { for(i=0; i<2; i++) *dst[i]=line_filtered[colf][i]>>16; } } // Vertical int lower_edge[4]; for (i=0; i<4; i++) { int padding = i<2 && (S.height & 1 ) ? 1 : 0; lower_edge[i]=height_d2 + radius + padding; } for(col=0; col>16; dst[i]+=dst_v_shift; } rowf++; } if (row == S.height-1) { for(i=0; i<2; i++) *dst[i]=line_filtered[rowf][i]>>16; } } free(line_filtered); } void LibRaw::wf_bayer4_block_filter(int* radius_list, void* src_image, int src_imgmode, void* dst_image, int dst_imgmode) { #define WF_BLOCKFILTER_MAXF 8 #define WF_BAYERSRC4(row,col,c) ((ushort(*)[4])src_image)[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][c] #define WF_BAYERSRC1(row,col) ((ushort*)src_image) [((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)] #define WF_BAYERDST4(row,col,c) ((ushort(*)[4])dst_image)[((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)][c] #define WF_BAYERDST1(row,col) ((ushort*)dst_image) [((row) >> IO.shrink)*S.iwidth + ((col) >> IO.shrink)] int filter_itrtns_num = 0; int block_radius [WF_BLOCKFILTER_MAXF]; int block_radius_x2 [WF_BLOCKFILTER_MAXF]; int block_radius_x2_p1[WF_BLOCKFILTER_MAXF]; int block_radius_max = 0; int block_radius_max_x2; int block_radius_max_x2_p1; for(int i=0; (i> IO.shrink; else if (src_imgmode == WF_IMGMODE_BAYER4PLANE) src_h_shift = 8 >> IO.shrink; src_v_shift = S.width*src_h_shift; if (dst_imgmode == WF_IMGMODE_BAYER1PLANE) dst_h_shift = 2 >> IO.shrink; else if (dst_imgmode == WF_IMGMODE_BAYER4PLANE) dst_h_shift = 8 >> IO.shrink; dst_v_shift = S.width*dst_h_shift; int width_d2 = S.width / 2; int height_d2 = S.height / 2; int width_p1_d2 = (S.width+1) / 2; int height_p1_d2 = (S.height+1) / 2; ushort *src[4], *dst[4]; long int (*src_plus)[4], (*src_minus)[4]; long int block_sum[4]; int row, col; int right_edge[4], lower_edge[4]; for(row=0; row