/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc.
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using Emgu.CV;
using Emgu.CV.CvEnum;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
public class OperationMask : Operation
{
#region Overrides
public override string IconClass => "fa-solid fa-mask";
public override string Title => "Mask";
public override string Description =>
"Mask the intensity of the LCD output using a greyscale input image.\n\n" +
"Useful to correct LCD light uniformity for a specific printer.\n\n" +
"NOTE: This operation should be run only after repairs and other transformations. The provided" +
"input mask image must match the output resolution of the target printer.";
public override string ConfirmationText =>
$"mask layers from {LayerIndexStart} through {LayerIndexEnd}";
public override string ProgressTitle =>
$"Masking layers from {LayerIndexStart} through {LayerIndexEnd}";
public override string ProgressAction => "Masked layers";
public override bool CanHaveProfiles => false;
public override string? ValidateInternally()
{
var sb = new StringBuilder();
if (!HaveInputMask)
{
sb.AppendLine("The mask can not be empty.");
}
return sb.ToString();
}
#endregion
#region Properties
[XmlIgnore]
public Mat? Mask { get; set; }
public bool HaveInputMask => Mask is not null;
#endregion
#region Constructor
public OperationMask() { }
public OperationMask(FileFormat slicerFile) : base(slicerFile) { }
#endregion
#region Methods
///
/// Loads mask from a image file
///
///
///
///
public void LoadFromFile(string filePath, bool invertMask = false, Size maskSize = default)
{
Mask = CvInvoke.Imread(filePath, ImreadModes.Grayscale);
if (maskSize is {Width: > 0, Height: > 0} && Mask.Size != maskSize)
{
CvInvoke.Resize(Mask, Mask, maskSize);
}
if (invertMask)
{
InvertMask();
}
}
public void InvertMask()
{
if (!HaveInputMask) return;
CvInvoke.BitwiseNot(Mask, Mask);
}
protected override bool ExecuteInternally(OperationProgress progress)
{
Parallel.For(LayerIndexStart, LayerIndexEnd + 1, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
using var mat = SlicerFile[layerIndex].LayerMat;
Execute(mat);
SlicerFile[layerIndex].LayerMat = mat;
progress.LockAndIncrement();
});
return !progress.Token.IsCancellationRequested;
}
public override bool Execute(Mat mat, params object[]? arguments)
{
var target = GetRoiOrDefault(mat);
using var mask = GetMask(mat);
if (Mask!.Size != target.Size) return false;
CvInvoke.BitwiseAnd(target, Mask, target, mask);
return true;
}
#endregion
}