SpencerOTF::SpencerOTF( const ViewingConditions &viewCond, int cols, int rows, float adaptationLuminance ) : filter( cols, rows ) { const float pixelsPerDeg = viewCond.getPixelsPerDegree( (viewCond.maxDistance + viewCond.minDistance)/2. ); std::cerr << "Creating OTF filter "; const float SCOTOPIC = 0.01, PHOTOPIC = 3; float w[4]; const float scotopicW[4] = { 0.282, 0.478, 0.207, 0.033 }; // const float mesopicW[4] = { 0.368, 0.478, 0.138, 0.016 }; const float photopicW[4] = { 0.384, 0.478, 0.138, 0 }; if( adaptationLuminance > PHOTOPIC ) { for( int i = 0; i < 4; i++ ) w[i] = photopicW[i]; std::cerr << "for photopic conditions.."; } else if( adaptationLuminance <= SCOTOPIC ) { for( int i = 0; i < 4; i++ ) w[i] = scotopicW[i]; std::cerr << "for scotopic conditions.."; } else { float ad = (log( adaptationLuminance ) - log( SCOTOPIC )) / (log(PHOTOPIC)-log(SCOTOPIC)); for( int i = 0; i < 4; i++ ) w[i] = scotopicW[i]*(1-ad) + photopicW[i]*ad; std::cerr << "for mesopic conditions.."; } pfs::Array2DImpl f( cols, rows ); pfs::Array2D *filterSpatial = filter.setSpatial(); pfs::setArray( filterSpatial, 0 ); createVisDegDigitalFilter( spencerPSF0, &f, pixelsPerDeg ); normalizeToSum( &f ); multiplyAndAddArray( filterSpatial, &f, w[0] ); createVisDegDigitalFilter( spencerPSF1, &f, pixelsPerDeg ); normalizeToSum( &f ); multiplyAndAddArray( filterSpatial, &f, w[1] ); createVisDegDigitalFilter( spencerPSF2, &f, pixelsPerDeg ); normalizeToSum( &f ); multiplyAndAddArray( filterSpatial, &f, w[2] ); createVisDegDigitalFilter( spencerPSF3_Y, &f, pixelsPerDeg ); normalizeToSum( &f ); multiplyAndAddArray( filterSpatial, &f, w[3] ); normalizeToSum( filterSpatial ); dumpImageAspect->dump( "filter_otf.pfs", filterSpatial, "Y" ); dumpImageAspect->dumpFrequency( "filter_otf_fft.pfs", &filter ); std::cerr << ".\n"; }
MultiAdaptationCSF::MultiAdaptationCSF( int cols, int rows, const ViewingConditions &viewCond ) { // TODO: Find min, max and decide what adaptation levels should be used // float amMin = 9999999; // float amMax = -9999999; // const int size = in->getRows()*in->getCols(); // for( int i = 0; i < size; i++ ) { // const float v = (*adaptationMap)(i); // if( amMin > v ) amMin = v; // else if( amMax < v ) amMax = v; // } // static const float templateAdaptationLevels[] = // { // 0.0001, 0.01, 0.1, 1, 10, 100 // }; // for( int i = 0; i < sizeof( templateAdaptationLevels ) / sizeof( float ); i++ ) { // if( templateAdaptationLevels[i+1] // } static const float templateAdaptationLevels[] = { 0.0001, 0.01, 0.1, 1, 10, 100 }; const int templateAdaptationLevelsCount = sizeof( templateAdaptationLevels ) / sizeof( float ); for( int i = 0; i < templateAdaptationLevelsCount; i++ ) adaptationLevels[i] = templateAdaptationLevels[i]; adaptationLevelsCount = templateAdaptationLevelsCount; //NOT compatible with new Cygwin version of gcc. //filters = new (pfs::Array2DImpl*)[adaptationLevelsCount]; filters = new pfs::Array2DImpl*[adaptationLevelsCount]; // Prepare CSF filters for( int i = 0; i < adaptationLevelsCount; i++ ) { // For each adaptation level std::cerr << "Creating CSF filter: "; // Build CSF filter for the viewing conditions filters[i] = new pfs::Array2DImpl( cols/2+1, rows/2+1 ); float meanObserverDistance = (viewCond.minDistance + viewCond.maxDistance)/2; float pixelsPerDeg = viewCond.getPixelsPerDegree( meanObserverDistance ); float imgSizeVD = ((float)viewCond.xResolution / pixelsPerDeg) * ((float)viewCond.yResolution / pixelsPerDeg); createCSFFilter( filters[i], adaptationLevels[i], imgSizeVD, pixelsPerDeg, meanObserverDistance, CSF_DALY_NORMALIZED ); } }
VDPCSF::VDPCSF( int cols, int rows, const ViewingConditions &viewCond, const float Y_adapt ) { // Build CSF filter for the viewing conditions filter = new pfs::Array2DImpl( cols/2+1, rows/2+1 ); float meanObserverDistance = (viewCond.minDistance + viewCond.maxDistance)/2; float pixelsPerDeg = viewCond.getPixelsPerDegree( meanObserverDistance ); float imgSizeVD = ((float)viewCond.xResolution / pixelsPerDeg) * ((float)viewCond.yResolution / pixelsPerDeg); createCSFFilter( filter, Y_adapt, imgSizeVD, pixelsPerDeg, meanObserverDistance, CSF_DALY ); dumpImageAspect->dump( "filter_csf.pfs", filter, "Y" ); }
WestheimerOTF::WestheimerOTF( const ViewingConditions &viewCond, int cols, int rows ) : filter( cols, rows ) { pfs::Array2D *filterSpatial = filter.setSpatial(); const float pixelsPerDeg = viewCond.getPixelsPerDegree( (viewCond.maxDistance + viewCond.minDistance)/2. ); std::cerr << "Creating OTF filter.."; WestheimerOTFFunc normannBaxterOTFFunc; createVisDegDigitalFilter( &normannBaxterOTFFunc, filterSpatial, pixelsPerDeg ); dumpImageAspect->dump( "filter_otf.pfs", filterSpatial, "Y" ); dumpImageAspect->dumpFrequency( "filter_otf_fft.pfs", &filter ); std::cerr << ".\n"; }
DeeleyOTF::DeeleyOTF( const ViewingConditions &viewCond, int cols, int rows, float adaptationLuminance ) { 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 ); float p_mm = p * 1e3; std::cerr << "Creating OTF filter.."; std::cerr << "(pupil diameter " << p_mm << "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; (*filter)(x,y) = expf( -pow(v/ (20.9 - 2.1*p_mm), 1.3-0.07*p_mm) ); } } (*filter)(0,0) = max( (*filter)(1,0), (*filter)(0,1) ); normalizeToMax( filter ); dumpImageAspect->dump( "filter_otf.pfs", filter, "Y" ); std::cerr << ".\n"; }
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-1), y_norm = 0.5 / (filterHeight-1); float dx, dy; int x, y; float p = getPupilDiameter( adaptationLuminance )/2; // Pupil radius in meters std::cerr << "Creating OTF filter.."; std::cerr << "(pupil diameter " << p*1e3*2 << "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"; }