public void Capture( ImageUtility.Bitmap _Source, CameraCalibrationDatabase _Database, CaptureParms _Parms )
{
if ( _Source == null )
throw new Exception( "Invalid source bitmap to build texture from!" );
if ( _Database == null )
throw new Exception( "Invalid calibration database found in parameters!" );
if ( _Parms == null )
throw new Exception( "Invalid calibration parameters!" );
if ( m_SwatchWidth <= 0 || m_SwatchHeight <= 0 )
throw new Exception( "Invalid swatch size! Must be > 0!" );
// Save parameters as they're associated to this texture
m_CaptureParameters = _Parms;
m_WhiteReflectanceReference = _Database.WhiteReflectanceReference;
m_WhiteReflectanceCorrectionFactor = _Database.WhiteReflectanceCorrectionFactor;
m_SpatialCorrectionEnabled = _Database.WhiteReferenceImage != null;
//////////////////////////////////////////////////////////////////////////
// Setup the database to find the most appropriate calibration data for our image infos
_Database.PrepareCalibrationFor( _Parms.ISOSpeed, _Parms.ShutterSpeed, _Parms.Aperture );
//////////////////////////////////////////////////////////////////////////
// Build target texture
ImageUtility.float4 AvgXYZ = new ImageUtility.float4( 0, 0, 0, 0 );
//DEBUG
// float MinLuminance_Raw = float.MaxValue;
// float MaxLuminance_Raw = -float.MaxValue;
const int EXTREME_VALUES_COUNT = 100;
ImageUtility.float3[] ArrayMin = new ImageUtility.float3[EXTREME_VALUES_COUNT];
ImageUtility.float3[] ArrayMax = new ImageUtility.float3[EXTREME_VALUES_COUNT];
for ( int i=0; i < EXTREME_VALUES_COUNT; i++ )
{
ArrayMin[i] = new ImageUtility.float3( 0, 1, 0 );
ArrayMax[i] = new ImageUtility.float3( 0, 0, 0 );
}
if ( _Parms.CropSource )
{
float fImageWidth = 2.0f * _Parms.CropRectangleHalfSize.x * _Source.Height;
float fImageHeight = 2.0f * _Parms.CropRectangleHalfSize.y * _Source.Height;
int W = (int) Math.Floor( fImageWidth );
int H = (int) Math.Floor( fImageHeight );
ImageUtility.float2 AxisX = new ImageUtility.float2( (float) Math.Cos( _Parms.CropRectangleRotation ), -(float) Math.Sin( _Parms.CropRectangleRotation ) );
ImageUtility.float2 AxisY = new ImageUtility.float2( (float) Math.Sin( _Parms.CropRectangleRotation ), (float) Math.Cos( _Parms.CropRectangleRotation ) );
ImageUtility.float2 TopLeftCorner = new ImageUtility.float2( 0.5f * (_Source.Width - _Source.Height) + _Parms.CropRectangleCenter.x * _Source.Height, _Source.Height * _Parms.CropRectangleCenter.y )
+ _Source.Height * (-_Parms.CropRectangleHalfSize.x * AxisX - _Parms.CropRectangleHalfSize.y * AxisY);
m_Texture = new ImageUtility.Bitmap( W, H, new ImageUtility.ColorProfile( ImageUtility.ColorProfile.STANDARD_PROFILE.sRGB ) );
ImageUtility.float4 XYZ;
ImageUtility.float3 ShortXYZ;
ImageUtility.float3 xyY;
ImageUtility.float2 CurrentScanlinePixel = TopLeftCorner + 0.5f * (fImageWidth - W) * AxisX + 0.5f * (fImageHeight - H) * AxisY;
if ( Math.Abs( _Parms.CropRectangleRotation ) < 1e-6f )
{ // Use integer pixels to avoid attenuated values due to bilinear filtering
CurrentScanlinePixel.x = (float) Math.Floor( CurrentScanlinePixel.x );
CurrentScanlinePixel.y = (float) Math.Floor( CurrentScanlinePixel.y );
}
for ( int Y=0; Y < H; Y++ )
{
ImageUtility.float2 CurrentPixel = CurrentScanlinePixel;
for ( int X=0; X < W; X++ )
{
float U = CurrentPixel.x / _Source.Width;
float V = CurrentPixel.y / _Source.Height;
XYZ = _Source.BilinearSample( CurrentPixel.x, CurrentPixel.y );
//DEBUG
// float L = XYZ.y * _Database.GetSpatialLuminanceCorrectionFactor( U, V );
// if ( L < MinLuminance_Raw )
// MinLuminance_Raw = L;
// if ( L > MaxLuminance_Raw )
// MaxLuminance_Raw = L;
//DEBUG
xyY = ImageUtility.ColorProfile.XYZ2xyY( (ImageUtility.float3) XYZ );
xyY = _Database.CalibrateWithSpatialCorrection( U, V, xyY ); // Apply luminance calibration
ShortXYZ = ImageUtility.ColorProfile.xyY2XYZ( xyY );
XYZ = new ImageUtility.float4( ShortXYZ, XYZ.w );
m_Texture.ContentXYZ[X,Y] = XYZ;
// Update min/max/avg values
InsertMinMax( ShortXYZ, ArrayMin, ArrayMax, EXTREME_VALUES_COUNT );
AvgXYZ += XYZ;
CurrentPixel += AxisX;
}
CurrentScanlinePixel += AxisY;
}
}
else
{ // Simple texture copy, with luminance calibration
m_Texture = new ImageUtility.Bitmap( _Source.Width, _Source.Height, new ImageUtility.ColorProfile( ImageUtility.ColorProfile.STANDARD_PROFILE.sRGB ) );
ImageUtility.float4 XYZ;
ImageUtility.float3 ShortXYZ;
ImageUtility.float3 xyY;
int W = m_Texture.Width;
int H = m_Texture.Height;
int X0 = 0;
int X1 = W;
int Y0 = 0;
int Y1 = H;
//DEBUG
// X0 = 1088; Y0 = 764;
// X1 = X0 + 1100; Y1 = Y0 + 632;
for ( int Y=Y0; Y < Y1; Y++ )
{
float V = (float) Y / H;
for ( int X=X0; X < X1; X++ )
{
float U = (float) X / W;
XYZ = _Source.ContentXYZ[X,Y];
//DEBUG
// float L = XYZ.y * _Database.GetSpatialLuminanceCorrectionFactor( U, V );
// if ( L < MinLuminance_Raw )
// MinLuminance_Raw = L;
// if ( L > MaxLuminance_Raw )
// MaxLuminance_Raw = L;
//DEBUG
xyY = ImageUtility.ColorProfile.XYZ2xyY( (ImageUtility.float3) XYZ );
xyY = _Database.CalibrateWithSpatialCorrection( U, V, xyY ); // Apply luminance calibration
ShortXYZ = ImageUtility.ColorProfile.xyY2XYZ( xyY );
XYZ = new ImageUtility.float4( ShortXYZ, XYZ.w );
m_Texture.ContentXYZ[X,Y] = XYZ;
// Update min/max/avg values
InsertMinMax( ShortXYZ, ArrayMin, ArrayMax, EXTREME_VALUES_COUNT );
AvgXYZ += XYZ;
}
}
}
// Normalize average swatch color
float Normalizer = 1.0f / (m_Texture.Width*m_Texture.Height);
ImageUtility.float3 avgxyY = ImageUtility.ColorProfile.XYZ2xyY( Normalizer * ((ImageUtility.float3) AvgXYZ) );
m_SwatchAvg.xyY = avgxyY;
// Compute min & max using statistical norm
ImageUtility.float3 BestXYZ_Min;
ImageUtility.float3 BestXYZ_Max;
if ( _Parms.UseModeInsteadOfMean )
{ // Use mode
BestXYZ_Min = ComputeMode( ArrayMin );
BestXYZ_Max = ComputeMode( ArrayMax );
}
else
{ // Use mean
BestXYZ_Min = ComputeMean( ArrayMin );
BestXYZ_Max = ComputeMean( ArrayMax );
}
m_SwatchMin.xyY = ImageUtility.ColorProfile.XYZ2xyY( BestXYZ_Min );
m_SwatchMax.xyY = ImageUtility.ColorProfile.XYZ2xyY( BestXYZ_Max );
m_SwatchMin.Texture = BuildSwatch( m_SwatchWidth, m_SwatchHeight, m_SwatchMin.xyY );
m_SwatchMax.Texture = BuildSwatch( m_SwatchWidth, m_SwatchHeight, m_SwatchMax.xyY );
m_SwatchAvg.Texture = BuildSwatch( m_SwatchWidth, m_SwatchHeight, m_SwatchAvg.xyY );
// Rebuild custom swatches
foreach ( CustomSwatch CS in m_CustomSwatches )
CS.Texture = BuildSwatch( m_SwatchWidth, m_SwatchHeight, CS.xyY );
//////////////////////////////////////////////////////////////////////////
// Feed some purely informational shot infos to the main texture, probably won't be saved anyway...
m_Texture.HasValidShotInfo = true;
m_Texture.ISOSpeed = _Parms.ISOSpeed;
m_Texture.ShutterSpeed = _Parms.ShutterSpeed;
m_Texture.Aperture = _Parms.Aperture;
}