Moved debayer and preprocessing parameters to class ProcParams for every single image. Added tab RAW for changing those parameters. Progress bar shows only load step (work to do)
547 lines
16 KiB
C++
547 lines
16 KiB
C++
/*
|
|
* This file is part of RawTherapee.
|
|
*
|
|
* Copyright (c) 2004-2010 Gabor Horvath <hgabor@rawtherapee.com>
|
|
*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include <stdimagesource.h>
|
|
#include <mytime.h>
|
|
#include <iccstore.h>
|
|
#define MAXVAL 0xffff
|
|
#define CLIP(a) ((a)>0?((a)<MAXVAL?(a):MAXVAL):0)
|
|
#include <curves.h>
|
|
|
|
#undef THREAD_PRIORITY_NORMAL
|
|
|
|
namespace rtengine {
|
|
|
|
extern const Settings* settings;
|
|
|
|
template<class T> void freeArray (T** a, int H) {
|
|
for (int i=0; i<H; i++)
|
|
delete [] a[i];
|
|
delete [] a;
|
|
}
|
|
template<class T> T** allocArray (int W, int H) {
|
|
|
|
T** t = new T*[H];
|
|
for (int i=0; i<H; i++)
|
|
t[i] = new T[W];
|
|
return t;
|
|
}
|
|
|
|
#define HR_SCALE 2
|
|
StdImageSource::StdImageSource () : ImageSource(), plistener(NULL), img(NULL) {
|
|
|
|
hrmap[0] = NULL;
|
|
hrmap[1] = NULL;
|
|
hrmap[2] = NULL;
|
|
needhr = NULL;
|
|
embProfile = NULL;
|
|
idata = NULL;
|
|
}
|
|
|
|
StdImageSource::~StdImageSource () {
|
|
|
|
delete idata;
|
|
|
|
if (hrmap[0]!=NULL) {
|
|
int dh = img->height/HR_SCALE;
|
|
freeArray<float>(hrmap[0], dh);
|
|
freeArray<float>(hrmap[1], dh);
|
|
freeArray<float>(hrmap[2], dh);
|
|
}
|
|
|
|
delete img;
|
|
|
|
if (needhr)
|
|
freeArray<char>(needhr, img->height);
|
|
}
|
|
|
|
int StdImageSource::load (Glib::ustring fname) {
|
|
|
|
fileName = fname;
|
|
|
|
img = new Image16 ();
|
|
if (plistener) {
|
|
plistener->setProgressStr ("Loading...");
|
|
plistener->setProgress (0.0);
|
|
img->setProgressListener (plistener);
|
|
}
|
|
|
|
int error = img->load (fname);
|
|
if (error) {
|
|
delete img;
|
|
img = NULL;
|
|
return error;
|
|
}
|
|
|
|
embProfile = img->getEmbeddedProfile ();
|
|
idata = new ImageData (fname);
|
|
|
|
if (plistener) {
|
|
plistener->setProgressStr ("Ready.");
|
|
plistener->setProgress (1.0);
|
|
}
|
|
|
|
wb = ColorTemp (1.0, 1.0, 1.0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void StdImageSource::transform (PreviewProps pp, int tran, int &sx1, int &sy1, int &sx2, int &sy2) {
|
|
|
|
int W = img->width;
|
|
int H = img->height;
|
|
int sw = W, sh = H;
|
|
if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
|
|
sw = H;
|
|
sh = W;
|
|
}
|
|
int ppx = pp.x, ppy = pp.y;
|
|
if (tran & TR_HFLIP)
|
|
ppx = sw - pp.x - pp.w;
|
|
if (tran & TR_VFLIP)
|
|
ppy = sh - pp.y - pp.h;
|
|
|
|
sx1 = ppx;
|
|
sy1 = ppy;
|
|
sx2 = ppx + pp.w;
|
|
sy2 = ppy + pp.h;
|
|
|
|
if ((tran & TR_ROT) == TR_R180) {
|
|
sx1 = W - ppx - pp.w;
|
|
sy1 = H - ppy - pp.h;
|
|
sx2 = sx1 + pp.w;
|
|
sy2 = sy1 + pp.h;
|
|
}
|
|
else if ((tran & TR_ROT) == TR_R90) {
|
|
sx1 = ppy;
|
|
sy1 = H - ppx - pp.w;
|
|
sx2 = sx1 + pp.h;
|
|
sy2 = sy1 + pp.w;
|
|
}
|
|
else if ((tran & TR_ROT) == TR_R270) {
|
|
sx1 = W - ppy - pp.h;
|
|
sy1 = ppx;
|
|
sx2 = sx1 + pp.h;
|
|
sy2 = sy1 + pp.w;
|
|
}
|
|
printf ("ppx %d ppy %d ppw %d pph %d s: %d %d %d %d\n",pp.x, pp.y,pp.w,pp.h,sx1,sy1,sx2,sy2);
|
|
}
|
|
|
|
void StdImageSource::getImage_ (ColorTemp ctemp, int tran, Image16* image, PreviewProps pp, bool first, HRecParams hrp) {
|
|
|
|
// compute channel multipliers
|
|
double rm, gm, bm;
|
|
ctemp.getMultipliers (rm, gm, bm);
|
|
rm = 1.0 / rm;
|
|
gm = 1.0 / gm;
|
|
bm = 1.0 / bm;
|
|
double mul_lum = 0.299*rm + 0.587*gm + 0.114*bm;
|
|
rm /= mul_lum;
|
|
gm /= mul_lum;
|
|
bm /= mul_lum;
|
|
|
|
int sx1, sy1, sx2, sy2;
|
|
transform (pp, tran, sx1, sy1, sx2, sy2);
|
|
|
|
int imwidth = (sx2 - sx1) / pp.skip + ((sx2 - sx1) % pp.skip > 0);
|
|
int imheight = (sy2 - sy1) / pp.skip + ((sy2 - sy1) % pp.skip > 0);
|
|
|
|
int istart = sy1, iend = sy2, ix = 0;
|
|
if (first) {
|
|
iend = istart;
|
|
while (iend<(sy1+sy2)/2) {
|
|
iend+=pp.skip;
|
|
}
|
|
}
|
|
else {
|
|
while (istart<(sy1+sy2)/2) {
|
|
ix++;
|
|
istart+=pp.skip;
|
|
}
|
|
}
|
|
|
|
int mtran = tran;
|
|
int skip = pp.skip;
|
|
|
|
unsigned short* red = new unsigned short[imwidth];
|
|
unsigned short* grn = new unsigned short[imwidth];
|
|
unsigned short* blue = new unsigned short[imwidth];
|
|
|
|
for (int i=istart; i<iend; i+=skip, ix++) {
|
|
for (int j=0,jx=sx1; j<imwidth; j++,jx+=pp.skip) {
|
|
red[j] = img->r[i][jx];
|
|
grn[j] = img->g[i][jx];
|
|
blue[j] = img->b[i][jx];
|
|
}
|
|
// if (hrp.enabled)
|
|
// hlRecovery (red, grn, blue, i, sx1, sx2, pp.skip);
|
|
|
|
if ((mtran & TR_ROT) == TR_R180)
|
|
for (int j=0; j<imwidth; j++) {
|
|
image->r[imheight-1-ix][imwidth-1-j] = (int)CLIP(rm*red[j]);
|
|
image->g[imheight-1-ix][imwidth-1-j] = (int)CLIP(gm*grn[j]);
|
|
image->b[imheight-1-ix][imwidth-1-j] = (int)CLIP(bm*blue[j]);
|
|
}
|
|
else if ((mtran & TR_ROT) == TR_R90)
|
|
for (int j=0,jx=sx1; j<imwidth; j++,jx+=skip) {
|
|
image->r[j][imheight-1-ix] = (int)CLIP(rm*red[j]);
|
|
image->g[j][imheight-1-ix] = (int)CLIP(gm*grn[j]);
|
|
image->b[j][imheight-1-ix] = (int)CLIP(bm*blue[j]);
|
|
}
|
|
else if ((mtran & TR_ROT) == TR_R270)
|
|
for (int j=0,jx=sx1; j<imwidth; j++,jx+=skip) {
|
|
image->r[imwidth-1-j][ix] = (int)CLIP(rm*red[j]);
|
|
image->g[imwidth-1-j][ix] = (int)CLIP(gm*grn[j]);
|
|
image->b[imwidth-1-j][ix] = (int)CLIP(bm*blue[j]);
|
|
}
|
|
else {
|
|
for (int j=0,jx=sx1; j<imwidth; j++,jx+=skip) {
|
|
image->r[ix][j] = (int)CLIP(rm*red[j]);
|
|
image->g[ix][j] = (int)CLIP(gm*grn[j]);
|
|
image->b[ix][j] = (int)CLIP(bm*blue[j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] red;
|
|
delete [] grn;
|
|
delete [] blue;
|
|
}
|
|
|
|
void StdImageSource::getImage (ColorTemp ctemp, int tran, Image16* image, PreviewProps pp, HRecParams hrp, ColorManagementParams cmp, RAWParams raw) {
|
|
|
|
MyTime t1,t2;
|
|
|
|
t1.set ();
|
|
|
|
// if (hrp.enabled==true && hrmap[0]==NULL)
|
|
// updateHLRecoveryMap ();
|
|
if (settings->dualThreadEnabled) {
|
|
Glib::Thread *thread1 = Glib::Thread::create(sigc::bind(sigc::mem_fun(*this, &StdImageSource::getImage_), ctemp, tran, image, pp, true, hrp), 0, true, true, Glib::THREAD_PRIORITY_NORMAL);
|
|
Glib::Thread *thread2 = Glib::Thread::create(sigc::bind(sigc::mem_fun(*this, &StdImageSource::getImage_), ctemp, tran, image, pp, false, hrp), 0, true, true, Glib::THREAD_PRIORITY_NORMAL);
|
|
thread1->join ();
|
|
thread2->join ();
|
|
}
|
|
else {
|
|
getImage_ (ctemp, tran, image, pp, true, hrp);
|
|
getImage_ (ctemp, tran, image, pp, false, hrp);
|
|
}
|
|
|
|
colorSpaceConversion (image, cmp, embProfile);
|
|
|
|
// Flip if needed
|
|
if (tran & TR_HFLIP)
|
|
hflip (image);
|
|
if (tran & TR_VFLIP)
|
|
vflip (image);
|
|
|
|
t2.set ();
|
|
}
|
|
|
|
void StdImageSource::colorSpaceConversion (Image16* im, ColorManagementParams cmp, cmsHPROFILE embedded) {
|
|
|
|
cmsHPROFILE in;
|
|
cmsHPROFILE out = iccStore.workingSpace (cmp.working);
|
|
if (cmp.input=="(embedded)" || cmp.input=="" || cmp.input=="(camera)") {
|
|
if (embedded)
|
|
in = embedded;
|
|
else
|
|
in = iccStore.getsRGBProfile ();
|
|
}
|
|
else if (cmp.input!="(none)") {
|
|
in = iccStore.getProfile (cmp.input);
|
|
if (in==NULL && embedded)
|
|
in = embedded;
|
|
else if (in==NULL)
|
|
in = iccStore.getsRGBProfile ();
|
|
else if (cmp.gammaOnInput)
|
|
for (int i=0; i<im->height; i++)
|
|
for (int j=0; j<im->width; j++) {
|
|
im->r[i][j] = CurveFactory::gamma (im->r[i][j]);
|
|
im->g[i][j] = CurveFactory::gamma (im->g[i][j]);
|
|
im->b[i][j] = CurveFactory::gamma (im->b[i][j]);
|
|
}
|
|
}
|
|
|
|
if (cmp.input!="(none)") {
|
|
lcmsMutex->lock ();
|
|
cmsHTRANSFORM hTransform = cmsCreateTransform (in, TYPE_RGB_16_PLANAR, out, TYPE_RGB_16_PLANAR, settings->colorimetricIntent, 0);
|
|
lcmsMutex->unlock ();
|
|
cmsDoTransform (hTransform, im->data, im->data, im->planestride/2);
|
|
cmsDeleteTransform(hTransform);
|
|
}
|
|
}
|
|
|
|
void StdImageSource::getFullSize (int& w, int& h, int tr) {
|
|
|
|
w = img->width;
|
|
h = img->height;
|
|
if ((tr & TR_ROT) == TR_R90 || (tr & TR_ROT) == TR_R270) {
|
|
w = img->height;
|
|
h = img->width;
|
|
}
|
|
}
|
|
|
|
void StdImageSource::getSize (int tran, PreviewProps pp, int& w, int& h) {
|
|
|
|
w = pp.w / pp.skip + (pp.w % pp.skip > 0);
|
|
h = pp.h / pp.skip + (pp.h % pp.skip > 0);
|
|
}
|
|
|
|
void StdImageSource::hflip (Image16* image) {
|
|
int width = image->width;
|
|
int height = image->height;
|
|
|
|
unsigned short* rowr = new unsigned short[width];
|
|
unsigned short* rowg = new unsigned short[width];
|
|
unsigned short* rowb = new unsigned short[width];
|
|
for (int i=0; i<height; i++) {
|
|
for (int j=0; j<width; j++) {
|
|
rowr[j] = image->r[i][width-1-j];
|
|
rowg[j] = image->g[i][width-1-j];
|
|
rowb[j] = image->b[i][width-1-j];
|
|
}
|
|
memcpy (image->r[i], rowr, width*sizeof(unsigned short));
|
|
memcpy (image->g[i], rowg, width*sizeof(unsigned short));
|
|
memcpy (image->b[i], rowb, width*sizeof(unsigned short));
|
|
}
|
|
delete [] rowr;
|
|
delete [] rowg;
|
|
delete [] rowb;
|
|
}
|
|
|
|
void StdImageSource::vflip (Image16* image) {
|
|
int width = image->width;
|
|
int height = image->height;
|
|
|
|
register unsigned short tmp;
|
|
for (int i=0; i<height/2; i++)
|
|
for (int j=0; j<width; j++) {
|
|
tmp = image->r[i][j];
|
|
image->r[i][j] = image->r[height-1-i][j];
|
|
image->r[height-1-i][j] = tmp;
|
|
tmp = image->g[i][j];
|
|
image->g[i][j] = image->g[height-1-i][j];
|
|
image->g[height-1-i][j] = tmp;
|
|
tmp = image->b[i][j];
|
|
image->b[i][j] = image->b[height-1-i][j];
|
|
image->b[height-1-i][j] = tmp;
|
|
}
|
|
}
|
|
/*
|
|
void hlRecovery (unsigned short* red, unsigned short* green, unsigned short* blue, int H, int W, int i, int sx1, int sx2, int skip, char** needhr, float** hrmap[3]);
|
|
void hlmultipliers (int** rec[3], int max[3], int dh, int dw);
|
|
|
|
void StdImageSource::updateHLRecoveryMap () {
|
|
|
|
// detect maximal pixel values
|
|
int maxr = 0, maxg = 0, maxb = 0;
|
|
for (int i=32; i<img->height-32; i++)
|
|
for (int j=32; j<img->width-32; j++) {
|
|
if (img->r[i][j] > maxr) maxr = img->r[i][j];
|
|
if (img->g[i][j] > maxg) maxg = img->g[i][j];
|
|
if (img->b[i][j] > maxb) maxb = img->b[i][j];
|
|
}
|
|
|
|
maxr = maxr * 19 / 20;
|
|
maxg = maxg * 19 / 20;
|
|
maxb = maxb * 19 / 20;
|
|
max[0] = maxr;
|
|
max[1] = maxg;
|
|
max[2] = maxb;
|
|
|
|
// downscale image
|
|
int dw = img->width/HR_SCALE;
|
|
int dh = img->height/HR_SCALE;
|
|
Image16* ds = new Image16 (dw, dh);
|
|
|
|
// overburnt areas
|
|
int** rec[3];
|
|
for (int i=0; i<3; i++)
|
|
rec[i] = allocArray<int> (dw, dh);
|
|
|
|
if (needhr)
|
|
freeArray<char>(needhr, img->height);
|
|
needhr = allocArray<char> (img->width, img->height);
|
|
|
|
for (int i=0; i<img->height; i++)
|
|
for (int j=0; j<img->width; j++)
|
|
if (img->r[i][j]>=max[0] || img->g[i][j]>=max[1] || img->b[i][j]>=max[2])
|
|
needhr[i][j] = 1;
|
|
else
|
|
needhr[i][j] = 0;
|
|
|
|
|
|
for (int i=0; i<ds->height; i++)
|
|
for (int j=0; j<ds->width; j++) {
|
|
int sumr = 0; int cr = 0;
|
|
int sumg = 0; int cg = 0;
|
|
int sumb = 0; int cb = 0;
|
|
for (int x=0; x<HR_SCALE; x++)
|
|
for (int y=0; y<HR_SCALE; y++) {
|
|
int ix = HR_SCALE*i+x;
|
|
int jy = HR_SCALE*j+y;
|
|
sumr += img->r[ix][jy];
|
|
if (img->r[ix][jy] < maxr) cr++;
|
|
sumg += img->g[ix][jy];
|
|
if (img->g[ix][jy] < maxg) cg++;
|
|
sumb += img->b[ix][jy];
|
|
if (img->b[ix][jy] < maxb) cb++;
|
|
}
|
|
if (cr<HR_SCALE*HR_SCALE) rec[0][i][j] = INT_MAX; else rec[0][i][j] = sumr / HR_SCALE/HR_SCALE;
|
|
if (cg<HR_SCALE*HR_SCALE) rec[1][i][j] = INT_MAX; else rec[1][i][j] = sumg / HR_SCALE/HR_SCALE;
|
|
if (cb<HR_SCALE*HR_SCALE) rec[2][i][j] = INT_MAX; else rec[2][i][j] = sumb / HR_SCALE/HR_SCALE;
|
|
ds->r[i][j] = sumr / HR_SCALE/HR_SCALE;
|
|
ds->g[i][j] = sumg / HR_SCALE/HR_SCALE;
|
|
ds->b[i][j] = sumb / HR_SCALE/HR_SCALE;
|
|
|
|
}
|
|
|
|
hlmultipliers (rec, max, dh, dw);
|
|
|
|
if (hrmap[0]!=NULL) {
|
|
freeArray<float> (hrmap[0], dh);
|
|
freeArray<float> (hrmap[1], dh);
|
|
freeArray<float> (hrmap[2], dh);
|
|
}
|
|
|
|
hrmap[0] = allocArray<float> (dw, dh);
|
|
hrmap[1] = allocArray<float> (dw, dh);
|
|
hrmap[2] = allocArray<float> (dw, dh);
|
|
|
|
for (int i=0; i<dh; i++)
|
|
for (int j=0; j<dw; j++) {
|
|
hrmap[0][i][j] = ds->r[i][j]>0 ? (double)rec[0][i][j] / ds->r[i][j] : 1.0;
|
|
hrmap[1][i][j] = ds->g[i][j]>0 ? (double)rec[1][i][j] / ds->g[i][j] : 1.0;
|
|
hrmap[2][i][j] = ds->b[i][j]>0 ? (double)rec[2][i][j] / ds->b[i][j] : 1.0;
|
|
}
|
|
|
|
delete ds;
|
|
|
|
freeArray<int> (rec[0], dh);
|
|
freeArray<int> (rec[1], dh);
|
|
freeArray<int> (rec[2], dh);
|
|
}
|
|
|
|
void StdImageSource::hlRecovery (unsigned short* red, unsigned short* green, unsigned short* blue, int i, int sx1, int sx2, int skip) {
|
|
|
|
rtengine::hlRecovery (red, green, blue, img->height, img->width, i, sx1, sx2, skip, needhr, hrmap);
|
|
}
|
|
*/
|
|
int StdImageSource::getAEHistogram (unsigned int* histogram, int& histcompr) {
|
|
|
|
histcompr = 3;
|
|
|
|
memset (histogram, 0, (65536>>histcompr)*sizeof(int));
|
|
|
|
for (int i=0; i<img->height; i++)
|
|
for (int j=0; j<img->width; j++) {
|
|
histogram[CurveFactory::igamma_srgb (img->r[i][j])>>histcompr]++;
|
|
histogram[CurveFactory::igamma_srgb (img->g[i][j])>>histcompr]++;
|
|
histogram[CurveFactory::igamma_srgb (img->b[i][j])>>histcompr]++;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
ColorTemp StdImageSource::getAutoWB () {
|
|
|
|
double avg_r = 0;
|
|
double avg_g = 0;
|
|
double avg_b = 0;
|
|
int n = 0;
|
|
int p = 6;
|
|
|
|
for (int i=1; i<img->height-1; i++)
|
|
for (int j=1; j<img->width-1; j++) {
|
|
if (img->r[i][j]>64000 || img->g[i][j]>64000 || img->b[i][j]>64000)
|
|
continue;
|
|
avg_r += intpow((double)img->r[i][j], p);
|
|
avg_g += intpow((double)img->g[i][j], p);
|
|
avg_b += intpow((double)img->b[i][j], p);
|
|
n++;
|
|
}
|
|
return ColorTemp (pow(avg_r/n, 1.0/p), pow(avg_g/n, 1.0/p), pow(avg_b/n, 1.0/p));
|
|
}
|
|
|
|
void StdImageSource::transformPixel (int x, int y, int tran, int& tx, int& ty) {
|
|
|
|
int W = img->width;
|
|
int H = img->height;
|
|
int sw = W, sh = H;
|
|
if ((tran & TR_ROT) == TR_R90 || (tran & TR_ROT) == TR_R270) {
|
|
sw = H;
|
|
sh = W;
|
|
}
|
|
|
|
int ppx = x, ppy = y;
|
|
if (tran & TR_HFLIP)
|
|
ppx = sw - 1 - x ;
|
|
if (tran & TR_VFLIP)
|
|
ppy = sh - 1 - y;
|
|
|
|
tx = ppx;
|
|
ty = ppy;
|
|
|
|
if ((tran & TR_ROT) == TR_R180) {
|
|
tx = W - 1 - ppx;
|
|
ty = H - 1 - ppy;
|
|
}
|
|
else if ((tran & TR_ROT) == TR_R90) {
|
|
tx = ppy;
|
|
ty = H - 1 - ppx;
|
|
}
|
|
else if ((tran & TR_ROT) == TR_R270) {
|
|
tx = W - 1 - ppy;
|
|
ty = ppx;
|
|
}
|
|
}
|
|
|
|
ColorTemp StdImageSource::getSpotWB (std::vector<Coord2D> red, std::vector<Coord2D> green, std::vector<Coord2D>& blue, int tran) {
|
|
|
|
int x; int y;
|
|
double reds = 0, greens = 0, blues = 0;
|
|
int rn = 0, gn = 0, bn = 0;
|
|
for (int i=0; i<red.size(); i++) {
|
|
transformPixel (red[i].x, red[i].y, tran, x, y);
|
|
if (x>=0 && y>=0 && x<img->width && y<img->height) {
|
|
reds += img->r[y][x];
|
|
// img->r[y][x]=0; // debug!!!
|
|
rn++;
|
|
}
|
|
transformPixel (green[i].x, green[i].y, tran, x, y);
|
|
if (x>=0 && y>=0 && x<img->width && y<img->height) {
|
|
greens += img->g[y][x];
|
|
// img->g[y][x]=0; // debug!!!
|
|
gn++;
|
|
}
|
|
transformPixel (blue[i].x, blue[i].y, tran, x, y);
|
|
if (x>=0 && y>=0 && x<img->width && y<img->height) {
|
|
blues += img->b[y][x];
|
|
// img->b[y][x]=0; // debug!!!
|
|
bn++;
|
|
}
|
|
}
|
|
double img_r, img_g, img_b;
|
|
wb.getMultipliers (img_r, img_g, img_b);
|
|
printf ("AVG: %g %g %g\n", reds/rn, greens/gn, blues/bn);
|
|
|
|
return ColorTemp (reds/rn*img_r, greens/gn*img_g, blues/bn*img_b);
|
|
}
|
|
}
|
|
|