MarimontOTF::MarimontOTF( const ViewingConditions &viewCond, int cols, int rows, float adaptationLuminance ) { const float n_p = 1.336; const float f_p = 22.2888e-3; const float D0 = n_p/f_p; const float c = 3.434e3; const float q1 = 1.7312; const float q2 = 0.63346; const float q3 = 0.21410; filter = new pfs::Array2DImpl( cols/2+1, rows/2+1 ); int filterWidth=filter->getCols(); int filterHeight=filter->getRows(); float meanObserverDistance = (viewCond.minDistance + viewCond.maxDistance)/2; float pix_per_deg = viewCond.getPixelsPerDegree( meanObserverDistance ); float x_norm = 0.5 / filterWidth, y_norm = 0.5 / filterHeight; float dx, dy; int x, y; float p = getPupilDiameter( adaptationLuminance ); std::cerr << "Creating OTF filter.."; std::cerr << "(pupil diameter " << p*1e3 << "mm for adaptation lum " << adaptationLuminance << "cd/m^2)"; for (y = 0, dy = 0.0; y < filterHeight; y ++, dy += y_norm) { for (x = 0, dx = 0.0; x < filterWidth; x ++, dx += x_norm) { float v = pix_per_deg * sqrtf((float)(dx * dx + dy * dy)); if( v == 0 ) v = 0.1; const int lefSize = sizeof( luminanceEfficiencyFunction ) / sizeof( SpectralSensitivity ); float H = 0; for( int l = 0; l < lefSize; l++ ) { const float lambda = luminanceEfficiencyFunction[l].lambda*1e-9; const float s = c*(lambda / (D0*p)) * v; const float D_lambda = q1 - (q2/(lambda*1e6-q3)); const float w20 = p*p/2 * D0 * D_lambda / (D0 + D_lambda); const float alpha = 4*M_PI/lambda * w20 * fabsf( s ); const float py_max = sqrtf( (1-(s/2))*(1-(s/2)) ); const float dpy = py_max/10; float h = 0; for( float py = 0; py <= py_max; py += dpy ) { h += 4/(M_PI*alpha)*sinf( alpha*(sqrtf(1 - py*py) - fabsf(s)/2) )*dpy; } H += h*luminanceEfficiencyFunction[l].weight; } (*filter)(x,y) = H * (0.3481+0.6519*exp(-0.1212*v)); } } (*filter)(0,0) = max( (*filter)(1,0), (*filter)(0,1) ); normalizeToMax( filter ); dumpImageAspect->dump( "filter_otf.pfs", filter, "Y" ); std::cerr << ".\n"; }