using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading;
using LensAdjustmentTool.Configuration;
using LensAdjustmentTool.Models;
using TIS.Imaging;
namespace LensAdjustmentTool.Processing
{
public sealed class LensAdjustmentProcessor
{
private int _mode = (int)AdjustmentMode.Focus;
private readonly object _focusMaxSync = new object();
private readonly object _apertureRoiSync = new object();
private double _focusMaximum;
private Rectangle _apertureCalculationRoi = AppSettings.ApertureCalculationRoi;
public AdjustmentMode Mode => (AdjustmentMode)Volatile.Read(ref _mode);
public void SetMode(AdjustmentMode mode, bool resetFocusMaximum)
{
Interlocked.Exchange(ref _mode, (int)mode);
if (mode == AdjustmentMode.Focus && resetFocusMaximum)
{
ResetFocusMaximum();
}
}
public void ResetFocusMaximum()
{
lock (_focusMaxSync)
{
_focusMaximum = 0.0;
}
}
public Rectangle MoveApertureCalculationRoi(int deltaX, int deltaY)
{
lock (_apertureRoiSync)
{
Rectangle displayRoi = AppSettings.ApertureDisplayRoi;
int x = Math.Max(
displayRoi.X,
Math.Min(_apertureCalculationRoi.X + deltaX, displayRoi.Right - _apertureCalculationRoi.Width));
int y = Math.Max(
displayRoi.Y,
Math.Min(_apertureCalculationRoi.Y + deltaY, displayRoi.Bottom - _apertureCalculationRoi.Height));
_apertureCalculationRoi = new Rectangle(
x,
y,
_apertureCalculationRoi.Width,
_apertureCalculationRoi.Height);
return _apertureCalculationRoi;
}
}
public FrameAnalysisResult Process(IFrameQueueBuffer frame)
{
if (frame == null) throw new ArgumentNullException(nameof(frame));
AdjustmentMode mode = Mode;
using (Bitmap wrapped = frame.CreateBitmapWrap())
{
if (wrapped.Width != AppSettings.ExpectedFrameWidth ||
wrapped.Height != AppSettings.ExpectedFrameHeight)
{
throw new InvalidOperationException(
$"Unexpected frame size: {wrapped.Width}x{wrapped.Height}. " +
$"Expected {AppSettings.ExpectedFrameWidth}x{AppSettings.ExpectedFrameHeight}.");
}
return mode == AdjustmentMode.Focus
? ProcessFocus(wrapped)
: ProcessAperture(wrapped);
}
}
private FrameAnalysisResult ProcessFocus(Bitmap fullFrame)
{
Bitmap roi = Clone24(fullFrame, AppSettings.FocusRoi);
try
{
double current = FocusAnalyzer.CalculateScore(roi);
double maximum;
lock (_focusMaxSync)
{
if (current > _focusMaximum)
{
_focusMaximum = current;
}
maximum = _focusMaximum;
}
// System.Drawing Rotate90FlipNone is a 90-degree clockwise rotation.
roi.RotateFlip(RotateFlipType.Rotate90FlipNone);
Bitmap display = roi;
roi = null;
return new FrameAnalysisResult(
AdjustmentMode.Focus,
display,
current,
maximum);
}
finally
{
roi?.Dispose();
}
}
private FrameAnalysisResult ProcessAperture(Bitmap fullFrame)
{
Bitmap display = Clone24(fullFrame, AppSettings.ApertureDisplayRoi);
try
{
Rectangle calculationRoi = GetApertureCalculationRoi();
Rectangle relativeCalc = new Rectangle(
calculationRoi.X - AppSettings.ApertureDisplayRoi.X,
calculationRoi.Y - AppSettings.ApertureDisplayRoi.Y,
calculationRoi.Width,
calculationRoi.Height);
using (Bitmap calculation = Clone24(display, relativeCalc))
{
double mean = ApertureAnalyzer.CalculateMeanLuminance(calculation);
using (Graphics g = Graphics.FromImage(display))
using (Pen pen = new Pen(Color.Red, 3.0f))
{
g.DrawRectangle(
pen,
relativeCalc.X,
relativeCalc.Y,
relativeCalc.Width - 1,
relativeCalc.Height - 1);
}
Bitmap ownedDisplay = display;
display = null;
return new FrameAnalysisResult(
AdjustmentMode.Aperture,
ownedDisplay,
mean,
mean - AppSettings.ApertureTarget);
}
}
finally
{
display?.Dispose();
}
}
private Rectangle GetApertureCalculationRoi()
{
lock (_apertureRoiSync)
{
return _apertureCalculationRoi;
}
}
private static Bitmap Clone24(Bitmap source, Rectangle roi)
{
Rectangle bounds = new Rectangle(0, 0, source.Width, source.Height);
if (!bounds.Contains(roi))
{
throw new ArgumentOutOfRangeException(
nameof(roi),
$"ROI {roi} is outside image bounds {bounds}.");
}
return source.Clone(roi, PixelFormat.Format24bppRgb);
}
}
}