1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using Emgu.CV;
using System;
using System.Drawing;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Serialization;
using Emgu.CV.CvEnum;
using Emgu.CV.DepthAI;
using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
[Serializable]
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
/// <summary>
/// Loads mask from a image file
/// </summary>
/// <param name="filePath"></param>
/// <param name="invertMask"></param>
/// <param name="maskSize"></param>
public void LoadFromFile(string filePath, bool invertMask = false, Size maskSize = default)
{
Mask = CvInvoke.Imread(filePath, ImreadModes.Grayscale);
if (maskSize.Width > 0 && maskSize.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 =>
{
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
}
|