00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <ReinhardLocalOperator.h>
00022 #include <Exceptions.h>
00023 #include <math.h>
00024
00025 #define OPERATOR_NAME "Reinhard Local Operator"
00026
00027 Q_EXPORT_PLUGIN2(TT_ReinhardLocalOperator, ReinhardLocalOperatorFactory)
00028
00029
00039 ReinhardLocalOperator::ReinhardLocalOperator() :
00040 inputImage(NULL),
00041 outputImage(NULL),
00042 width(0),
00043 height(0),
00044 keyValue(0.18),
00045 sharpening(8.0)
00046 {
00047 for(int k=0; k<9; ++k)
00048 {
00049 responses[k] = NULL;
00050 }
00051 }
00052
00053 ReinhardLocalOperator::~ReinhardLocalOperator()
00054 {
00055 for(int k=0; k<9; ++k)
00056 {
00057 delete[] responses[k];
00058 }
00059 }
00060
00064 QString ReinhardLocalOperator::name() const
00065 {
00066 return tr(OPERATOR_NAME);
00067 }
00068
00069 void ReinhardLocalOperator::setupUi(QWidget* parent)
00070 {
00071 ui.setupUi(parent);
00072
00073 connect(ui.keyValueSlider, SIGNAL(sliderReleased()), this, SLOT(toneMap()));
00074 connect(ui.keyValueSlider, SIGNAL(valueChanged(int)), this, SLOT(updateKeyValue(int)));
00075
00076 connect(ui.sharpeningSlider, SIGNAL(sliderReleased()), this, SLOT(toneMap()));
00077 connect(ui.sharpeningSlider, SIGNAL(valueChanged(int)), this, SLOT(updateSharpening(int)));
00078 }
00079
00080 const HdrImage* ReinhardLocalOperator::getToneMappedImage() const
00081 {
00082 return outputImage;
00083 }
00084
00098 void ReinhardLocalOperator::setImage(const HdrImage* _inputImage)
00099 {
00100 if (!_inputImage->hasY())
00101 throw Exception(tr("Image passed to %1 does not contains Y data. Cannot turn water into wine.").arg(name()));
00102
00103 QTime t;
00104 t.start();
00105
00106 inputImage = _inputImage;
00107 QSize size = inputImage->size();
00108 width = size.width();
00109 height = size.height();
00110 int Y = inputImage->YIndex();
00111 int length = width*height;
00112 double delta = 0.0001;
00113 fftw_plan plan;
00114 fftw_complex* image = NULL;
00115 fftw_complex* filters[9] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
00116
00117 delete outputImage;
00118 outputImage = new HdrImage(*inputImage);
00119
00120
00121 avLum = 0.0;
00122 for(int i=0; i<height; ++i)
00123 for(int j=0; j<width; ++j)
00124 {
00125 double lum = qMax(double((*inputImage)[i][j][Y]),0.0);
00126 avLum += log(delta + lum);
00127 }
00128 avLum = exp(avLum/(double)(length));
00129
00130
00131 for(int k=0; k<9; ++k)
00132 {
00133 delete[] responses[k];
00134 }
00135
00136
00137 image = new fftw_complex[length];
00138 for(int k=0; k<9; ++k)
00139 {
00140 filters[k] = new fftw_complex[length];
00141 for(int i=0; i<length; ++i)
00142 {
00143 filters[k][i][0] = 0.0;
00144 filters[k][i][1] = 0.0;
00145 }
00146 }
00147
00148
00149 for(int k=0; k<9; ++k)
00150 {
00151 double s = pow(1.6, k);
00152 int rs = int(s+0.5);
00153 int maxY = qMin(height/2,rs);
00154 int minY = -maxY;
00155 int maxX = qMin(width/2,rs);
00156 int minX = -maxX;
00157 double c = (1./8.) * s * s;
00158 double a = 1./(M_PI * c);
00159 double sum = 0.0;
00160 double gauss = 0.0;
00161
00162 for(int y=minY; y<maxY; ++y)
00163 for(int x=minX; x<maxX; ++x)
00164 {
00165 gauss = a * exp(-(x*x + y*y)/c);
00166 filters[k][(y + height/2)*width + x + width/2][0] = gauss;
00167 sum += gauss;
00168 }
00169
00170
00171 for(int y=minY; y<maxY; ++y)
00172 for(int x=minX; x<maxX; ++x)
00173 {
00174 filters[k][(y + height/2)*width + x + width/2][0] /= sum;
00175 }
00176 }
00177
00178
00179 for(int k=0; k<9; ++k)
00180 {
00181 plan = fftw_plan_dft_2d(height, width, filters[k], filters[k], FFTW_FORWARD, FFTW_ESTIMATE);
00182 fftw_execute(plan);
00183
00184 double scale = 1./sqrt(length);
00185 for(int i=0; i<length; ++i)
00186 {
00187 filters[k][i][0] *= scale;
00188 filters[k][i][1] *= scale;
00189 }
00190 }
00191
00192
00193 for(int i=0; i<height; ++i)
00194 {
00195 for(int j=0; j<width; ++j)
00196 {
00197 image[i*width + j][0] = (*inputImage)[i][j][Y];
00198 image[i*width + j][1] = 0.0;
00199 }
00200 }
00201
00202
00203 plan = fftw_plan_dft_2d(height, width, image, image, FFTW_FORWARD, FFTW_ESTIMATE);
00204 fftw_execute(plan);
00205 double scale = 1./sqrt(length);
00206 for(int i=0; i<length; ++i)
00207 {
00208 image[i][0] *= scale;
00209 image[i][1] *= scale;
00210 }
00211
00212
00213 for(int k=0; k<9; ++k)
00214 {
00215 responses[k] = convolveFft(image, filters[k]);
00216 }
00217
00218
00219 for(int k=0; k<9; ++k)
00220 {
00221 plan = fftw_plan_dft_2d(height, width, responses[k], responses[k], FFTW_BACKWARD, FFTW_ESTIMATE);
00222 fftw_execute(plan);
00223
00224 for(int i=0; i<height/2; ++i)
00225 {
00226 for(int j=0; j<width/2; ++j)
00227 {
00228 double tmp = responses[k][i*width + j][0];
00229 responses[k][i*width + j][0] = responses[k][(i+height/2)*width + j+width/2][0];
00230 responses[k][(i+height/2)*width + j+width/2][0] = tmp;
00231
00232 tmp = responses[k][(i+height/2)*width + j][0];
00233 responses[k][(i+height/2)*width + j][0] = responses[k][i*width + j+width/2][0];
00234 responses[k][i*width + j+width/2][0] = tmp;
00235 }
00236 }
00237 }
00238
00239 fftw_destroy_plan(plan);
00240 delete[] image;
00241 for(int k=0; k<9; ++k)
00242 delete[] filters[k];
00243
00244 ui.keyValueSlider->setValue(18);
00245 ui.sharpeningSlider->setValue(8);
00246
00247 msg = tr("Operator Init: %1 ms").arg(t.elapsed());
00248
00249 toneMap();
00250 }
00251
00256 fftw_complex* ReinhardLocalOperator::convolveFft(fftw_complex* f1, fftw_complex* f2)
00257 {
00258 int length = width*height;
00259 fftw_complex* res = reinterpret_cast<fftw_complex*>(fftw_malloc(sizeof(fftw_complex)*length));
00260 for (int i=0; i<length; ++i)
00261 {
00262 res[i][0] = f1[i][0] * f2[i][0] - f1[i][1] * f2[i][1];
00263 res[i][1] = f1[i][0] * f2[i][1] + f1[i][1] * f2[i][0];
00264 }
00265 return res;
00266 }
00267
00273 void ReinhardLocalOperator::toneMap()
00274 {
00275 if(inputImage)
00276 {
00277 QTime t;
00278 t.start();
00279
00280 int Y = inputImage->YIndex();
00281 const float threshold = 0.05;
00282
00283 int k;
00284 double s, v, v1, v2;
00285 double lumMap = keyValue/avLum;
00286 double parameters = pow(2.0,sharpening) * keyValue;
00287 for(int i=0; i<height; ++i)
00288 {
00289 for(int j=0; j<width; ++j)
00290 {
00291 int index = i*width+j;
00292 for(k=0; k<8; ++k)
00293 {
00294 v1 = responses[k][index][0] * lumMap;
00295 v2 = responses[k+1][index][0] * lumMap;
00296 s = pow(1.6, k);
00297 v = (v1 - v2) / ((parameters / (s*s)) + v1);
00298 if (qAbs(v) > threshold)
00299 {
00300 break;
00301 }
00302 }
00303 (*outputImage)[i][j][Y] = ((*inputImage)[i][j][Y] * lumMap) / (1.0 + v1);
00304 }
00305 }
00306
00307 emit message(msg + tr(" Tone Mapping: %1 ms").arg(t.elapsed()));
00308
00309 emit imageUpdated();
00310 }
00311 }
00312
00318 void ReinhardLocalOperator::updateKeyValue(int value)
00319 {
00320 keyValue = double(value)/100.0;
00321 ui.keyValue->setText(QString("%1").arg(keyValue, 0, 'f', 2));
00322 }
00323
00329 void ReinhardLocalOperator::updateSharpening(int value)
00330 {
00331 sharpening = double(value);
00332 ui.sharpening->setText(QString("%1").arg(sharpening));
00333 }
00334
00335
00336
00337
00348 ToneMappingOperatorPtr ReinhardLocalOperatorFactory::createOperator() const
00349 {
00350 return ToneMappingOperatorPtr(new ReinhardLocalOperator);
00351 }
00352
00356 QString ReinhardLocalOperatorFactory::operatorName() const
00357 {
00358 return tr(OPERATOR_NAME);
00359 }