参考广泛使用的CalculateRamp代码: Changing image contrast with GDI+ and C#
public static void CalculateRamp(double level, double gamma, double brightness, double contrast)
{
ramp.Red = new ushort[256];
ramp.Green = new ushort[256];
ramp.Blue = new ushort[256];
gamma /= 10;
brightness = 1 + (((brightness - 50) / 100) * 65535);
contrast = 1 + ((contrast - 50) / 100);
level = 1 + ((level - 50) / 100);
for (int i = 0; i < 256; i++)
{
double value = i * 256;
value = (Math.Pow(value / 65535, 1 / gamma) * 65535) + 0.5;
value = ((((value / 65535) - 0.5) * contrast) + 0.5) * 65535;
value = value += brightness;
value *= level;
ramp.Red[i] = ramp.Green[i] = ramp.Blue[i] = (ushort)Math.Min((double)65535, Math.Max((double)0, value));
}
SetDeviceGammaRamp(GetDC(IntPtr.Zero), ref ramp);
}
如何直接从现有的Gamma Ramp计算此方法的输入值(参数)?换句话说,反向方法是什么?
如果我要向用户提供这些实用程序,我需要在进行调整之前向他们提供当前设置。
我已经尝试了三天来提出解决方案,并且必须承认这超出了我的能力范围。
2016年9月8日更新 - 我最好的解决方案......
虽然我长期努力地寻找数学解决方案,但我终于回过头来使用迭代方法。
计算每个可能设置然后检查匹配的钝力迭代将运行超过20分钟;这是不可接受的。
我发现我可以在数组中间取一个样本点,然后快速迭代所有可能的设置以找到该样本的匹配项;然后执行数组的所有256个计算,看看我们是否完全匹配。此操作通常在不到一秒的时间内完成。
但我意识到还有另一个问题。如果没有使用CalculateRamp算法(例如出厂设置)编程Gamma Ramp,那么我们不太可能找到与我们的设置完全匹配的内容;我们的解决方案将失败。
因此,我设计了一个备用计划,该计划使用来自伽马斜坡阵列的四个采样点;然后设置一个可接受值的“跨度”,它将为我们的设置提供近似的近似值。每次失败后,“span”的标准会变宽,并重新进入搜索范围。这种情况通常会在大约三秒内产生Gamma斜坡设置的近似值。这是可以接受的,因为我的主程序可以在程序启动序列期间执行此操作。
这是我的C ++解决方案......
//---------------------------------------------------------------------------
int __fastcall TForm1::Round( double flt ){
int num = (int) flt;
if( flt >= 0 ){
if( flt - num >= .5 ){ num++;}
}else{
if( flt - num <= -.5 ){ num--;}
}
return( num );
}
//----------------------------------------------------------------------------
void __fastcall TForm1::GetGammaRampSettings(void){
// level should be between 2 and 100
// gamma should be between 2 and 50
// brigntess should be between 0 and 100
// contrast should be between 0 and 100
double i,j,k,m;
double gamma,bright,cntrst,level;
double v1,v2,v3,v4;
double c1,c2,c3,c4;
double a1,a2,a3,a4,b1,b2,b3,b4; //for best estimate criteria
int x1,x2,x3,x4,x5;
int d,n;
WORD span = 8;
bool tog = false;
TDateTime strt, end;
WORD GammaArray[3][256]; //Gamma Ramp Array buffer
WORD CompArray[256]; //Array buffer for comparison
HDC GammaDC = GetDC( NULL ); //fetch the gamma ramp array
GetDeviceGammaRamp( GammaDC, GammaArray );
ReleaseDC( NULL, GammaDC );
strt=Now();
//Find two endpoints for the GammaArray
for( x1 = 0; x1 < 256; x1++ ){
if( GammaArray[0][x1] > 0 ){ break;}
}
for( x2 = 0; x2 < 256; x2++ ){
if( GammaArray[0][x2] == 65535 ){ break;}
}
if( x2 == 256 ){ x2 = 255; }
x5 = x1 + Round( (double)( x2 - x1 ) / 2.0 );
Memo1->Lines->Add( "x1 = " + IntToStr( x1 )); //start
Memo1->Lines->Add( "x2 = " + IntToStr( x2 )); //end
Memo1->Lines->Add( "x5 = " + IntToStr( x5 )); //mid
//neutral settings are as follows...
//Bright = 50
//Contrast = 50
//Level = 50
//Gamma = 10
//The following code will look for an exact match to the settings
//but it may fail if the gamma ramp was not programmed using the
//original CalculateRamp code. ie: A factory setting, or other
for(n=0;n<=40;){//gamma
if( n < 9 ){ //gamma begins with a neutral setting of 10 and
if( tog ){ //then increments high and low depending on tog
i = ( 10 - n );
tog = false; n++;
if( i == 10 ){ continue;}
}else{
i = ( 10 + n );
tog = true;
}
}else{
i = ( 10 + n++ );
}
gamma = i / 10;
v1 = ( pow( (double)(x5 * 256) / 65535, 1 / gamma) * 65535) + 0.5;
for( j = 2; j < 101 ; j++ ){//level //980,000 possible iterations
level = 1 + ((j - 50) / 100);
for( k = 0; k < 101; k++ ){//bright
bright = 1 + (((k - 50) / 100) * 65535);
for( m = 0; m < 101; m++ ){//contrast
cntrst = 1 + ((m - 50) / 100);
//Test only the mid-line value at first
//To see if a complete comparison is warranted
c1 = (((( v1 / 65535 ) - 0.5) * cntrst) + 0.5) * 65535;
c1 = c1 += bright;
c1 *= level;
if( c1 > 65535){ c1 = 65535; }
if( c1 < 0 ){ c1 = 0; }
if( (WORD) c1 == GammaArray[0][x5] ){
for( d = 0; d < 256; d++ ){
c1 = ( pow( (double)(d * 256) / 65535, 1 / gamma) * 65535) + 0.5;
c1 = ((( (c1 / 65535) - 0.5) * cntrst) + 0.5) * 65535;
c1 = c1 += bright;
c1 *= level;
if( c1 > 65535 ){ c1 = 65535;}
if( c1 < 0 ){ c1 = 0;}
CompArray[d] = (WORD) c1;
}
if(memcmp( &CompArray[0], &GammaArray[0][0], 2*256) == 0){
goto ENDIT;
}
}
}
}
}
}
//Since an exact match could not be found for the current
//gamma ramp settings, we will broaden our criteria and
//search for the best match for our gamma ramp settings.
//Here our search is based on the start and end values,
//plus two additional values that are 1/4 of the overall distance
//above the start point; and 1/4 the distance below the end point.
Memo1->Lines->Add("Values NOT Discovered!!!");
Memo1->Lines->Add("Widening the Search....");
x3 = x1 + Round( (double)( x2 - x1 ) / 4.0 );
x4 = x1 + Round(( (double)( x2 - x1 ) / 4.0 )*3);
Memo1->Lines->Add( "x1 = " + IntToStr( x1 )); //start
Memo1->Lines->Add( "x2 = " + IntToStr( x2 )); //end
Memo1->Lines->Add( "x3 = " + IntToStr( x3 )); // 1/4
Memo1->Lines->Add( "x4 = " + IntToStr( x4 )); // 3/4
BROADEN:
a1 = GammaArray[0][x1]+span; //these are our high and low
b1 = GammaArray[0][x1]-span; //values that the ramp values must
//fall within to be accepted
a2 = GammaArray[0][x2]+span;
b2 = GammaArray[0][x2]-span; //span begins at 8 and increments upwards
//with each loop, ever widening the
a3 = GammaArray[0][x3]+span; //the search criteria
b3 = GammaArray[0][x3]-span;
a4 = GammaArray[0][x4]+span;
b4 = GammaArray[0][x4]-span;
tog=false;
for(n=0;n<=40;){//gamma
if( n < 9 ){ //gamma begins with a neutral setting of 10 and
if( tog ){ //then increments high and low depending on tog
i = ( 10 - n );
tog = false; n++;
if( i == 10 ){ continue;} //prevents two instances of i == 10
}else{
i = ( 10 + n );
tog = true;
}
}else{
i = ( 10 + n++ );
}
gamma = i / 10;
v1 = ( pow( (double)(x1 * 256) / 65535, 1 / gamma) * 65535) + 0.5;
v2 = ( pow( (double)(x2 * 256) / 65535, 1 / gamma) * 65535) + 0.5;
v3 = ( pow( (double)(x3 * 256) / 65535, 1 / gamma) * 65535) + 0.5;
v4 = ( pow( (double)(x4 * 256) / 65535, 1 / gamma) * 65535) + 0.5;
for( j = 2; j < 101 ; j++ ){//level //980,000 possible iterations
level = 1 + ((j - 50) / 100);
for( k = 0; k < 101; k++ ){//bright
bright = 1 + (((k - 50) / 100) * 65535);
for( m = 0; m < 101; m++ ){//contrast
cntrst = 1 + ((m - 50) / 100);
//Test the four sample values to see if the
//results falls within our acceptance criteria.
c1 = (((( v1 / 65535 ) - 0.5) * cntrst) + 0.5) * 65535;
c1 = c1 += bright;
c1 *= level;
c2 = (((( v2 / 65535 ) - 0.5) * cntrst) + 0.5) * 65535;
c2 = c2 += bright;
c2 *= level;
c3 = (((( v3 / 65535 ) - 0.5) * cntrst) + 0.5) * 65535;
c3 = c3 += bright;
c3 *= level;
c4 = (((( v4 / 65535 ) - 0.5) * cntrst) + 0.5) * 65535;
c4 = c4 += bright;
c4 *= level;
if( c1 <= a1 && c1 >= b1 && c2 <= a2 && c2 >= b2 &&
c3 <= a3 && c3 >= b3 && c4 <= a4 && c4 >= b4 ){
Memo1->Lines->Add("Best Estimate +/- " + IntToStr( span ));
goto ENDIT;
}
}
}
}
}
if( span < 256){
span *= 2;
goto BROADEN;
}else if( span <= 4096 ){ //set the maximum allowance for "span"
span += 128; //which is the basis for the allowable
goto BROADEN; //search criteria
}
//if we get to this point then we have utterly failed.
Memo1->Lines->Add("Values STILL NOT Discovered.");
Memo1->Lines->Add("GetGammaRampSettings Failed.");
return;
ENDIT:
end=Now();
Memo1->Lines->Add("Execution duration was "+
FormatDateTime("nn:ss:zzz",end-strt));
//Report on findings...
Memo1->Lines->Add("Bright = " + IntToStr( (int) k ));
Memo1->Lines->Add("Cntrst = " + IntToStr( (int) m ));
Memo1->Lines->Add("Level = " + IntToStr( (int) j ));
Memo1->Lines->Add("Gamma = " + IntToStr( (int) i ));
}
//---------------------------------------------------------------------------