public void UpdateBitmap() { if (m_Bitmap == null) { return; } int W = m_Bitmap.Width; int H = m_Bitmap.Height; using (Graphics G = Graphics.FromImage(m_Bitmap)) { using (SolidBrush B = new SolidBrush(Color.Black)) G.FillRectangle(B, 0, 0, W, H); if (m_CameraCalibration != null && m_Thumbnail != null) { // Draw thumbnail RectangleF ClientRect = ImageClientRect(); G.DrawImage(m_Thumbnail, ClientRect, new RectangleF(0, 0, m_Thumbnail.Width, m_Thumbnail.Height), GraphicsUnit.Pixel); // Draw probe measurement circles if available for (int ProbeIndex = 0; ProbeIndex < 6; ProbeIndex++) { CameraCalibration.Probe P = m_CameraCalibration.m_Reflectances[ProbeIndex]; if (!P.m_MeasurementDiscIsAvailable) { continue; } PointF ClientPos = ImageUV2Client(new PointF(P.m_MeasurementCenterX, P.m_MeasurementCenterY)); float ClientRadius = ClientRect.Width * P.m_MeasurementRadius; // G.DrawEllipse( m_PenProbeShadow, ClientPos.X - ClientRadius, ClientPos.Y - ClientRadius, 2*ClientRadius, 2*ClientRadius ); G.DrawEllipse(P.m_IsAvailable ? m_PenProbe : m_PenProbeInvalid, ClientPos.X - ClientRadius, ClientPos.Y - ClientRadius, 2 * ClientRadius, 2 * ClientRadius); } } } Invalidate(); }
/// <summary> /// Prepares the interpolated calibration table to process the pixels in an image shot with the specified shot infos /// </summary> /// <param name="_ISOSpeed"></param> /// <param name="_ShutterSpeed"></param> /// <param name="_Aperture"></param> public void PrepareCalibrationFor(float _ISOSpeed, float _ShutterSpeed, float _Aperture) { if (m_RootNode == null) { throw new Exception("Calibration grid hasn't been built: did you provide a valid database path? Does the path contain camera calibration data?"); } if (IsPreparedFor(_ISOSpeed, _ShutterSpeed, _Aperture)) { return; // Already prepared! } ////////////////////////////////////////////////////////////////////////// // Find the 8 nodes encompassing our values // I'm making the delicate assumption that, although the starting node is chosen on the // condition its EV values are strictly inferior to the target we're looking for, all // neighbor nodes should satisfy the condition they're properly placed. // // This is true for the direct neighbors +X, +Y, +Z that are immediately above target values // but for example, neighbor (+X +Y) may have a very bad aperture value (Z) that may be // above the target aperture... // // Let's hope the user won't provide too fancy calibrations... // (anyway, interpolants are clamped in [0,1] so there's no risk of overshooting) // ImageUtility.float3 EV; GridNode.Convert2EV(_ISOSpeed, _ShutterSpeed, _Aperture, out EV.x, out EV.y, out EV.z); // Find the start node GridNode StartNode = FindStartNode(EV.x, EV.y, EV.z); m_InterpolationStartNode = StartNode; // Build the 8 grid nodes from it GridNode[,,] Grid = new GridNode[2, 2, 2]; Grid[0, 0, 0] = StartNode; Grid[1, 0, 0] = StartNode.m_Neighbors[0][1] != null ? StartNode.m_Neighbors[0][1] : StartNode; // +X Grid[0, 1, 0] = StartNode.m_Neighbors[1][1] != null ? StartNode.m_Neighbors[1][1] : StartNode; // +Y Grid[0, 0, 1] = StartNode.m_Neighbors[2][1] != null ? StartNode.m_Neighbors[2][1] : StartNode; // +Z Grid[1, 1, 0] = Grid[1, 0, 0].m_Neighbors[1][1] != null ? Grid[1, 0, 0].m_Neighbors[1][1] : Grid[1, 0, 0]; // +X +Y Grid[0, 1, 1] = Grid[0, 1, 0].m_Neighbors[2][1] != null ? Grid[0, 1, 0].m_Neighbors[2][1] : Grid[0, 1, 0]; // +Y +Z Grid[1, 0, 1] = Grid[0, 0, 1].m_Neighbors[0][1] != null ? Grid[0, 0, 1].m_Neighbors[0][1] : Grid[0, 0, 1]; // +X +Z Grid[1, 1, 1] = Grid[1, 1, 0].m_Neighbors[2][1] != null ? Grid[1, 1, 0].m_Neighbors[2][1] : Grid[1, 1, 0]; // +X +Y +Z ////////////////////////////////////////////////////////////////////////// // Create the successive interpolants for trilinear interpolation // // Assume we interpolate on X first (ISO speed), so we need 4 distinct values ImageUtility.float4 tX = new ImageUtility.float4( Math.Max(0.0f, Math.Min(1.0f, (EV.x - Grid[0, 0, 0].m_EV_ISOSpeed) / Math.Max(1e-6f, Grid[1, 0, 0].m_EV_ISOSpeed - Grid[0, 0, 0].m_EV_ISOSpeed))), // Y=0 Z=0 Math.Max(0.0f, Math.Min(1.0f, (EV.x - Grid[0, 1, 0].m_EV_ISOSpeed) / Math.Max(1e-6f, Grid[1, 1, 0].m_EV_ISOSpeed - Grid[0, 1, 0].m_EV_ISOSpeed))), // Y=1 Z=0 Math.Max(0.0f, Math.Min(1.0f, (EV.x - Grid[0, 0, 1].m_EV_ISOSpeed) / Math.Max(1e-6f, Grid[1, 0, 1].m_EV_ISOSpeed - Grid[0, 0, 1].m_EV_ISOSpeed))), // Y=0 Z=1 Math.Max(0.0f, Math.Min(1.0f, (EV.x - Grid[0, 1, 1].m_EV_ISOSpeed) / Math.Max(1e-6f, Grid[1, 1, 1].m_EV_ISOSpeed - Grid[0, 1, 1].m_EV_ISOSpeed))) // Y=1 Z=1 ); ImageUtility.float4 rX = new ImageUtility.float4(1.0f - tX.x, 1.0f - tX.y, 1.0f - tX.z, 1.0f - tX.w); // Compute the 4 interpolated shutter speeds & apertures ImageUtility.float4 ShutterSpeedsX = new ImageUtility.float4( rX.x * Grid[0, 0, 0].m_EV_ShutterSpeed + tX.x * Grid[1, 0, 0].m_EV_ShutterSpeed, // Y=0 Z=0 rX.y * Grid[0, 1, 0].m_EV_ShutterSpeed + tX.y * Grid[1, 1, 0].m_EV_ShutterSpeed, // Y=1 Z=0 rX.z * Grid[0, 0, 1].m_EV_ShutterSpeed + tX.z * Grid[1, 0, 1].m_EV_ShutterSpeed, // Y=0 Z=1 rX.w * Grid[0, 1, 1].m_EV_ShutterSpeed + tX.w * Grid[1, 1, 1].m_EV_ShutterSpeed // Y=1 Z=1 ); ImageUtility.float4 AperturesX = new ImageUtility.float4( rX.x * Grid[0, 0, 0].m_EV_Aperture + tX.x * Grid[1, 0, 0].m_EV_Aperture, // Y=0 Z=0 rX.y * Grid[0, 1, 0].m_EV_Aperture + tX.y * Grid[1, 1, 0].m_EV_Aperture, // Y=1 Z=0 rX.z * Grid[0, 0, 1].m_EV_Aperture + tX.z * Grid[1, 0, 1].m_EV_Aperture, // Y=0 Z=1 rX.w * Grid[0, 1, 1].m_EV_Aperture + tX.w * Grid[1, 1, 1].m_EV_Aperture // Y=1 Z=1 ); // Next we interpolate on Y (Shutter speed), so we need 2 distinct values ImageUtility.float2 tY = new ImageUtility.float2( Math.Max(0.0f, Math.Min(1.0f, (EV.y - ShutterSpeedsX.x) / Math.Max(1e-6f, ShutterSpeedsX.y - ShutterSpeedsX.x))), // Z=0 Math.Max(0.0f, Math.Min(1.0f, (EV.y - ShutterSpeedsX.z) / Math.Max(1e-6f, ShutterSpeedsX.w - ShutterSpeedsX.z))) // Z=1 ); ImageUtility.float2 rY = new ImageUtility.float2(1.0f - tY.x, 1.0f - tY.y); // Compute the 2 apertures ImageUtility.float2 AperturesY = new ImageUtility.float2( rY.x * AperturesX.x + tY.x * AperturesX.y, rY.y * AperturesX.z + tY.y * AperturesX.w ); // Finally, we interpolate on Z (Aperture), we need only 1 single value float tZ = Math.Max(0.0f, Math.Min(1.0f, (EV.z - AperturesY.x) / Math.Max(1e-6f, AperturesY.y - AperturesY.x))); float rZ = 1.0f - tZ; ////////////////////////////////////////////////////////////////////////// // Create the special camera calibration that is the result of the interpolation of the 8 nearest ones in the grid m_InterpolatedCalibration = new CameraCalibration(); m_InterpolatedCalibration.m_CameraShotInfos.m_ISOSpeed = _ISOSpeed; m_InterpolatedCalibration.m_CameraShotInfos.m_ShutterSpeed = _ShutterSpeed; m_InterpolatedCalibration.m_CameraShotInfos.m_Aperture = _Aperture; for (int ProbeIndex = 0; ProbeIndex < REQUIRED_PROBES_COUNT; ProbeIndex++) { CameraCalibration.Probe TargetProbe = m_InterpolatedCalibration.m_Reflectances[ProbeIndex]; float L000 = Grid[0, 0, 0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured; float L100 = Grid[1, 0, 0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured; float L010 = Grid[0, 1, 0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured; float L110 = Grid[1, 1, 0].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured; float L001 = Grid[0, 0, 1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured; float L101 = Grid[1, 0, 1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured; float L011 = Grid[0, 1, 1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured; float L111 = Grid[1, 1, 1].m_CameraCalibration.m_Reflectances[ProbeIndex].m_LuminanceMeasured; // Interpolate on X (ISO speed) float L00 = rX.x * L000 + tX.x * L100; float L10 = rX.y * L010 + tX.y * L110; float L01 = rX.z * L001 + tX.z * L101; float L11 = rX.w * L011 + tX.w * L111; // Interpolate on Y (shutter speed) float L0 = rY.x * L00 + tY.x * L10; float L1 = rY.y * L01 + tY.y * L11; // Interpolate on Z (aperture) float L = rZ * L0 + tZ * L1; TargetProbe.m_IsAvailable = true; TargetProbe.m_LuminanceMeasured = L; } // Fill missing values m_InterpolatedCalibration.UpdateAllLuminances(); // Reset white reflectance reference because it was set for another setup WhiteReflectanceReference = new ImageUtility.float3(0, 0, -1); }