/*
* This file is part of RawTherapee.
*
* RawTherapee is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* RawTherapee is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with RawTherapee. If not, see .
*
* © 2010 Emil Martinec
*
*/
#include
#include
#include "curves.h"
#include "labimage.h"
#include "improcfun.h"
#include "rawimagesource.h"
#include "rt_math.h"
#ifdef _OPENMP
#include
#endif
#define CLIPC(a) ((a)>-32000?((a)<32000?(a):32000):-32000)
#define DIRWT(i1,j1,i,j) (rangefn[abs((int)data_fine->L[i1][j1]-data_fine->L[i][j])+abs((int)data_fine->a[i1][j1]-data_fine->a[i][j])+abs((int)data_fine->b[i1][j1]-data_fine->b[i][j])] )
namespace rtengine {
static const int maxlevel = 4;
//sequence of scales
static const int scales[8] = {1,2,4,8,16,32,64,128};
//sequence of pitches
static const int pitches[8] = {1,1,1,1,1,1,1,1};
//sequence of scales
//static const int scales[8] = {1,1,1,1,1,1,1,1};
//sequence of pitches
//static const int pitches[8] = {2,2,2,2,2,2,2,2};
//sequence of scales
//static const int scales[8] = {1,3,6,10,15,21,28,36};
//sequence of pitches
//static const int pitches[8] = {1,1,1,1,1,1,1,1};
//sequence of scales
//static const int scales[8] = {1,1,2,4,8,16,32,64};
//sequence of pitches
//static const int pitches[8] = {2,1,1,1,1,1,1,1};
//pitch is spacing of subsampling
//scale is spacing of directional averaging weights
//example 1: no subsampling at any level -- pitch=1, scale=2^n
//example 2: subsampling by 2 every level -- pitch=2, scale=1 at each level
//example 3: no subsampling at first level, subsampling by 2 thereafter --
// pitch =1, scale=1 at first level; pitch=2, scale=2 thereafter
void ImProcFunctions :: dirpyrLab_equalizer(LabImage * src, LabImage * dst, /*float luma, float chroma, float gamma*/ const double * mult )
{
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
LUTf rangefn(0x20000);
//set up weights
float noise = 1500;
//set up range functions
for (int i=0; i<0x20000; i++)
rangefn[i] = (int)((noise/((double)i + noise)));
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
int level;
int ** buffer[3];
LabImage * dirpyrLablo[maxlevel];
int w = src->W;
int h = src->H;
buffer[0] = allocArray (w+128, h+128);
buffer[1] = allocArray (w+128, h+128);
buffer[2] = allocArray (w+128, h+128);
for (int i=0; iH; i+=totalpitch, i1++)
for(int j = 0, j1=0; j < src->W; j+=totalpitch, j1++) {
//copy pixels
buffer[0][i][j] = dirpyrLablo[maxlevel-1]->L[i1][j1];
buffer[1][i][j] = dirpyrLablo[maxlevel-1]->a[i1][j1];
buffer[2][i][j] = dirpyrLablo[maxlevel-1]->b[i1][j1];
}
//if we are not subsampling, this is lots faster but does the typecasting work???
//memcpy(buffer[0],dirpyrLablo[maxlevel-1]->L,sizeof(buffer[0]));
//memcpy(buffer[1],dirpyrLablo[maxlevel-1]->a,sizeof(buffer[1]));
//memcpy(buffer[2],dirpyrLablo[maxlevel-1]->b,sizeof(buffer[2]));
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for(int level = maxlevel - 1; level > 0; level--)
{
//int scale = scales[level];
int pitch = pitches[level];
totalpitch /= pitch;
idirpyr_eq(dirpyrLablo[level], dirpyrLablo[level-1], buffer, level, pitch, totalpitch, mult );
}
scale = scales[0];
pitch = pitches[0];
totalpitch /= pitch;
idirpyr_eq(dirpyrLablo[0], dst, buffer, 0, pitch, totalpitch, mult );
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for (int i=0; iH; i++)
for (int j=0; jW; j++) {
// TODO: Is integer cast necessary here?
dst->L[i][j] = CLIP((int)( buffer[0][i][j] ));
dst->a[i][j] = CLIPC((int)( buffer[1][i][j] ));
dst->b[i][j] = CLIPC((int)( buffer[2][i][j] ));
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for(int i = 0; i < maxlevel; i++)
{
delete dirpyrLablo[i];
}
for (int c=0;c<3;c++)
freeArray(buffer[c], h+128);
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
}
void ImProcFunctions::dirpyr_eq(LabImage* data_fine, LabImage* data_coarse, LUTf & rangefn, int level, int pitch, int scale, const double * mult )
{
//pitch is spacing of subsampling
//scale is spacing of directional averaging weights
//example 1: no subsampling at any level -- pitch=1, scale=2^n
//example 2: subsampling by 2 every level -- pitch=2, scale=1 at each level
//example 3: no subsampling at first level, subsampling by 2 thereafter --
// pitch =1, scale=1 at first level; pitch=2, scale=2 thereafter
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
// calculate weights, compute directionally weighted average
int width = data_fine->W;
int height = data_fine->H;
//generate domain kernel
int halfwin = 1;//min(ceil(2*sig),3);
int scalewin = halfwin*scale;
#ifdef _OPENMP
#pragma omp parallel for
#endif
for(int i = 0; i < height; i+=pitch) { int i1=i/pitch;
for(int j = 0, j1=0; j < width; j+=pitch, j1++)
{
float Lout, aout, bout;
float norm;
norm = 0;//if we do want to include the input pixel in the sum
Lout = 0;
aout = 0;
bout = 0;
for(int inbr=max(0,i-scalewin); inbr<=min(height-1,i+scalewin); inbr+=scale) {
for (int jnbr=max(0,j-scalewin); jnbr<=min(width-1,j+scalewin); jnbr+=scale) {
float dirwt = DIRWT(inbr, jnbr, i, j);
Lout += dirwt*data_fine->L[inbr][jnbr];
aout += dirwt*data_fine->a[inbr][jnbr];
bout += dirwt*data_fine->b[inbr][jnbr];
norm += dirwt;
}
}
data_coarse->L[i1][j1]=Lout/norm;//low pass filter
data_coarse->a[i1][j1]=aout/norm;
data_coarse->b[i1][j1]=bout/norm;
}
}
}
//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
void ImProcFunctions::idirpyr_eq(LabImage* data_coarse, LabImage* data_fine, int *** buffer, int level, int pitch, int scale, const double * mult )
{
int width = data_fine->W;
int height = data_fine->H;
float lumamult[4], chromamult[4];
for (int i=0; i<4; i++) {
lumamult[i] = mult[i];
chromamult[i] = mult[i+4];
}
float wtdsum[6], norm, dirwt;
float hipass[3];
int i1, j1;
// for coarsest level, take non-subsampled lopass image and subtract from lopass_fine to generate hipass image
// denoise hipass image, add back into lopass_fine to generate denoised image at fine scale
// now iterate:
// (1) take denoised image at level n, expand and smooth using gradient weights from lopass image at level n-1
// the result is the smoothed image at level n-1
// (2) subtract smoothed image at level n-1 from lopass image at level n-1 to make hipass image at level n-1
// (3) denoise the hipass image at level n-1
// (4) add the denoised image at level n-1 to the smoothed image at level n-1 to make the denoised image at level n-1
// note that the coarsest level amounts to skipping step (1) and doing (2,3,4).
// in other words, skip step one if pitch=1
if (pitch==1) {
// step (1-2-3-4)
#ifdef _OPENMP
#pragma omp parallel for
#endif
for(int i = 0; i < height; i++)
for(int j = 0; j < width; j++) {
//luma
float hipass0 = (float)data_fine->L[i][j]-data_coarse->L[i][j];
buffer[0][i*scale][j*scale] += hipass0 * lumamult[level];//*luma;
//chroma
float hipass1 = data_fine->a[i][j]-data_coarse->a[i][j];
float hipass2 = data_fine->b[i][j]-data_coarse->b[i][j];
buffer[1][i*scale][j*scale] += hipass1 * chromamult[level]; //*chroma;
buffer[2][i*scale][j*scale] += hipass2 * chromamult[level]; //*chroma;
}
} else {
// step (1)
//if (pitch>1), pitch=2; expand coarse image, fill in missing data
LabImage* smooth;
smooth = new LabImage(width, height);
#ifdef _OPENMP
#pragma omp parallel
#endif
{
#ifdef _OPENMP
#pragma omp for
#endif
for(int i = 0; i < height; i+=pitch){ int i2=i/pitch;
for(int j = 0, j2=0; j < width; j+=pitch, j2++) {
//copy common pixels
smooth->L[i][j] = data_coarse->L[i2][j2];
smooth->a[i][j] = data_coarse->a[i2][j2];
smooth->b[i][j] = data_coarse->b[i2][j2];
}
}
#ifdef _OPENMP
#pragma omp for
#endif
for(int i = 0; i < height-1; i+=2)
for(int j = 0; j < width-1; j+=2) {
//do midpoint first
norm=dirwt=0;
wtdsum[0]=wtdsum[1]=wtdsum[2]=wtdsum[3]=wtdsum[4]=wtdsum[5]=0.0;
for(i1=i; i1L[i1][j1];
wtdsum[1] += dirwt*smooth->a[i1][j1];
wtdsum[2] += dirwt*smooth->b[i1][j1];
wtdsum[3] += dirwt*buffer[0][i1*scale][j1*scale];// not completely right if j1*scale or i1*scale is out of bounds of original image ???
wtdsum[4] += dirwt*buffer[1][i1*scale][j1*scale];// also should we use directional average?
wtdsum[5] += dirwt*buffer[2][i1*scale][j1*scale];
norm+=dirwt;
}
norm = 1/norm;
smooth->L[i+1][j+1]=wtdsum[0]*norm;
smooth->a[i+1][j+1]=wtdsum[1]*norm;
smooth->b[i+1][j+1]=wtdsum[2]*norm;
buffer[0][(i+1)*scale][(j+1)*scale]=wtdsum[3]*norm;
buffer[1][(i+1)*scale][(j+1)*scale]=wtdsum[4]*norm;
buffer[2][(i+1)*scale][(j+1)*scale]=wtdsum[5]*norm;
}
#ifdef _OPENMP
#pragma omp for
#endif
for(int i = 0; i < height-1; i+=2)
for(int j = 0; j < width-1; j+=2) {
//now right neighbor
if (j+1==width) continue;
norm=dirwt=0;
wtdsum[0]=wtdsum[1]=wtdsum[2]=wtdsum[3]=wtdsum[4]=wtdsum[5]=0.0;
for (j1=j; j1L[i][j1];
wtdsum[1] += dirwt*smooth->a[i][j1];
wtdsum[2] += dirwt*smooth->b[i][j1];
wtdsum[3] += dirwt*buffer[0][i*scale][j1*scale];
wtdsum[4] += dirwt*buffer[1][i*scale][j1*scale];
wtdsum[5] += dirwt*buffer[2][i*scale][j1*scale];
norm+=dirwt;
}
for (i1=max(0,i-1); i1L[i1][j+1];
wtdsum[1] += dirwt*smooth->a[i1][j+1];
wtdsum[2] += dirwt*smooth->b[i1][j+1];
wtdsum[3] += dirwt*buffer[0][i1*scale][(j+1)*scale];
wtdsum[4] += dirwt*buffer[1][i1*scale][(j+1)*scale];
wtdsum[5] += dirwt*buffer[2][i1*scale][(j+1)*scale];
norm+=dirwt;
}
norm = 1/norm;
smooth->L[i][j+1]=wtdsum[0]*norm;
smooth->a[i][j+1]=wtdsum[1]*norm;
smooth->b[i][j+1]=wtdsum[2]*norm;
buffer[0][i][(j+1)*scale]=wtdsum[3]*norm;
buffer[1][i][(j+1)*scale]=wtdsum[4]*norm;
buffer[2][i][(j+1)*scale]=wtdsum[5]*norm;
//now down neighbor
if (i+1==height) continue;
norm=0;
wtdsum[0]=wtdsum[1]=wtdsum[2]=wtdsum[3]=wtdsum[4]=wtdsum[5]=0.0;
for (i1=i; i1L[i1][j];
wtdsum[1] += dirwt*smooth->a[i1][j];
wtdsum[2] += dirwt*smooth->b[i1][j];
wtdsum[3] += dirwt*buffer[0][i1*scale][j*scale];
wtdsum[4] += dirwt*buffer[1][i1*scale][j*scale];
wtdsum[5] += dirwt*buffer[2][i1*scale][j*scale];
norm+=dirwt;
}
for (j1=max(0,j-1); j1L[i+1][j1];
wtdsum[1] += dirwt*smooth->a[i+1][j1];
wtdsum[2] += dirwt*smooth->b[i+1][j1];
wtdsum[3] += dirwt*buffer[0][(i+1)*scale][j1*scale];
wtdsum[4] += dirwt*buffer[1][(i+1)*scale][j1*scale];
wtdsum[5] += dirwt*buffer[2][(i+1)*scale][j1*scale];
norm+=dirwt;
}
norm=1/norm;
smooth->L[i+1][j]=wtdsum[0]*norm;
smooth->a[i+1][j]=wtdsum[1]*norm;
smooth->b[i+1][j]=wtdsum[2]*norm;
buffer[0][(i+1)*scale][j*scale]=wtdsum[3]*norm;
buffer[1][(i+1)*scale][j*scale]=wtdsum[4]*norm;
buffer[2][(i+1)*scale][j*scale]=wtdsum[5]*norm;
}
// step (2-3-4)
#ifdef _OPENMP
#pragma omp for
#endif
for(int i = 0; i < height; i++)
for(int j = 0; j < width; j++) {
//luma
hipass[0] = (float)data_fine->L[i][j]-smooth->L[i][j];
buffer[0][i*scale][j*scale] += hipass[0] * lumamult[level]; //*luma;
//chroma
hipass[1] = data_fine->a[i][j]-smooth->a[i][j];
hipass[2] = data_fine->b[i][j]-smooth->b[i][j];
buffer[1][i*scale][j*scale] += hipass[1] * chromamult[level]; //*chroma;
buffer[2][i*scale][j*scale] += hipass[2] * chromamult[level]; //*chroma;
}
} // end parallel
delete smooth;
}
}
#undef DIRWT_L
#undef DIRWT_AB
#undef NRWT_L
#undef NRWT_AB
}