diff options
Diffstat (limited to 'UVtools.Core/Operations/OperationPixelArithmetic.cs')
-rw-r--r-- | UVtools.Core/Operations/OperationPixelArithmetic.cs | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/UVtools.Core/Operations/OperationPixelArithmetic.cs b/UVtools.Core/Operations/OperationPixelArithmetic.cs new file mode 100644 index 0000000..0ce74e8 --- /dev/null +++ b/UVtools.Core/Operations/OperationPixelArithmetic.cs @@ -0,0 +1,362 @@ +/* + * 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 System; +using System.ComponentModel; +using System.Text; +using System.Threading.Tasks; +using Emgu.CV; +using Emgu.CV.CvEnum; +using Emgu.CV.Structure; +using UVtools.Core.Extensions; +using UVtools.Core.FileFormats; + +namespace UVtools.Core.Operations +{ + [Serializable] + public class OperationPixelArithmetic : Operation + { + #region Members + private PixelArithmeticOperators _operator = PixelArithmeticOperators.Set; + private byte _value = byte.MaxValue; + private ThresholdType _thresholdType = ThresholdType.Binary; + private byte _thresholdMaxValue = 255; + private bool _affectBackPixels; + + #endregion + + #region Enums + public enum PixelArithmeticOperators : byte + { + [Description("Set: to a brightness")] + Set, + [Description("Add: with a brightness")] + Add, + [Description("Subtract: with a brightness")] + Subtract, + [Description("Multiply: with a brightness")] + Multiply, + [Description("Divide: with a brightness")] + Divide, + //[Description("Exponential: pixels by a brightness")] + //Exponential, + [Description("Minimum: set to a brightness if is lower than the current pixel")] + Minimum, + [Description("Maximum: set to a brightness if is higher than the current pixel")] + Maximum, + [Description("Bitwise Not: invert pixels")] + BitwiseNot, + [Description("Bitwise And: with a brightness")] + BitwiseAnd, + [Description("Bitwise Or: with a brightness")] + BitwiseOr, + [Description("Bitwise Xor: with a brightness")] + BitwiseXor, + [Description("AbsDiff: perform a absolute difference between pixel and brightness")] + AbsDiff, + [Description("Threshold: between a minimum/maximum brightness")] + Threshold, + [Description("Keep Region: in the selected ROI or masks")] + KeepRegion, + [Description("Discard Region: in the selected ROI or masks")] + DiscardRegion + } + #endregion + + #region Overrides + public override string Title => "Pixel arithmetic"; + + public override string Description => + "Perform arithmetic operations over the pixels"; + + public override string ConfirmationText => + $"arithmetic {_operator}" + + (ValueEnabled ? $"={_value}" : string.Empty) + + (_operator is PixelArithmeticOperators.Threshold ? $"/{_thresholdMaxValue}" : string.Empty) + + $" layers from {LayerIndexStart} through {LayerIndexEnd}"; + + public override string ProgressTitle => + $"Arithmetic {_operator}"+ + (ValueEnabled ? $"={_value}" : string.Empty) + +$" layers from {LayerIndexStart} through {LayerIndexEnd}"; + + public override string ProgressAction => "Calculated layers"; + + public override string ValidateInternally() + { + var sb = new StringBuilder(); + if (_operator == PixelArithmeticOperators.KeepRegion && !HaveROI && !HaveMask) + { + sb.AppendLine("The 'Keep' operator requires selected ROI/masks."); + } + else if (_operator == PixelArithmeticOperators.DiscardRegion && !HaveROI && !HaveMask) + { + sb.AppendLine("The 'Discard' operator requires selected ROI/masks."); + } + else if (_operator + is PixelArithmeticOperators.Add + or PixelArithmeticOperators.Subtract + or PixelArithmeticOperators.Maximum + or PixelArithmeticOperators.BitwiseOr + or PixelArithmeticOperators.BitwiseXor + or PixelArithmeticOperators.AbsDiff + && _value == 0) + /*|| + (_operator is PixelArithmeticOperators.Exponential && _value == 1) + )*/ + { + sb.AppendLine($"{_operator} by {_value} will have no effect."); + } + else if (_operator == PixelArithmeticOperators.Divide && _value == 0) + { + sb.AppendLine("Can't divide by 0."); + } + + return sb.ToString(); + } + + public override string ToString() + { + var result = $"[{_operator}: {_value}] [ABP: {_affectBackPixels}]" + + LayerRangeString; + if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}"; + return result; + } + #endregion + + #region Properties + + public PixelArithmeticOperators Operator + { + get => _operator; + set + { + if(!RaiseAndSetIfChanged(ref _operator, value)) return; + RaisePropertyChanged(nameof(ValueEnabled)); + RaisePropertyChanged(nameof(ThresholdEnabled)); + RaisePropertyChanged(nameof(AffectBackPixelsEnabled)); + } + } + + public byte Value + { + get => _value; + set + { + if(!RaiseAndSetIfChanged(ref _value, value)) return; + RaisePropertyChanged(nameof(ValuePercent)); + } + } + + // 255 - 100 + //value - x + public float ValuePercent => (float) Math.Round(_value * 100f / byte.MaxValue, 2); + + public bool ValueEnabled => _operator + is not PixelArithmeticOperators.BitwiseNot + and not PixelArithmeticOperators.KeepRegion + and not PixelArithmeticOperators.DiscardRegion + ; + + public ThresholdType ThresholdType + { + get => _thresholdType; + set => RaiseAndSetIfChanged(ref _thresholdType, value); + } + + public byte ThresholdMaxValue + { + get => _thresholdMaxValue; + set => RaiseAndSetIfChanged(ref _thresholdMaxValue, value); + } + + public bool ThresholdEnabled => _operator is PixelArithmeticOperators.Threshold; + + public bool AffectBackPixels + { + get => _affectBackPixels; + set => RaiseAndSetIfChanged(ref _affectBackPixels, value); + } + + public bool AffectBackPixelsEnabled => _operator + is not PixelArithmeticOperators.Subtract + and not PixelArithmeticOperators.Multiply + and not PixelArithmeticOperators.Divide + and not PixelArithmeticOperators.BitwiseNot + and not PixelArithmeticOperators.BitwiseAnd + and not PixelArithmeticOperators.KeepRegion + and not PixelArithmeticOperators.DiscardRegion + and not PixelArithmeticOperators.Threshold + ; + + #endregion + + #region Constructor + + public OperationPixelArithmetic() { } + + public OperationPixelArithmetic(FileFormat slicerFile) : base(slicerFile) { } + + #endregion + + #region Methods + + protected override bool ExecuteInternally(OperationProgress progress) + { + var tempMat = GetTempMat(); + + Parallel.For(LayerIndexStart, LayerIndexEnd + 1, layerIndex => + { + if (progress.Token.IsCancellationRequested) return; + using (var mat = SlicerFile[layerIndex].LayerMat) + { + Execute(mat, tempMat); + SlicerFile[layerIndex].LayerMat = mat; + } + + progress.LockAndIncrement(); + }); + + tempMat?.Dispose(); + + return !progress.Token.IsCancellationRequested; + } + + public override bool Execute(Mat mat, params object[] arguments) + { + using var original = mat.Clone(); + var target = GetRoiOrDefault(mat); + + Mat tempMat; + bool needDispose = false; + if (arguments is not null && arguments.Length > 0) + { + tempMat = arguments[0] as Mat; + } + else + { + tempMat = GetTempMat(); + needDispose = true; + } + + switch (_operator) + { + case PixelArithmeticOperators.Set: + tempMat.CopyTo(target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.Add: + CvInvoke.Add(target, tempMat, target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.Subtract: + CvInvoke.Subtract(target, tempMat, target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.Multiply: + CvInvoke.Multiply(target, tempMat, target); + break; + case PixelArithmeticOperators.Divide: + CvInvoke.Divide(target, tempMat, target); + break; + /*case PixelArithmeticOperators.Exponential: + CvInvoke.Pow(target, _value, tempMat); + if(!_affectBackPixels) ApplyMask(original, mat, original); + break;*/ + case PixelArithmeticOperators.Minimum: + CvInvoke.Min(target, tempMat, target); + if (!_affectBackPixels) ApplyMask(original, target, original); + break; + case PixelArithmeticOperators.Maximum: + CvInvoke.Max(target, tempMat, target); + if (!_affectBackPixels) ApplyMask(original, target, original); + break; + case PixelArithmeticOperators.BitwiseNot: + CvInvoke.BitwiseNot(target, target); + break; + case PixelArithmeticOperators.BitwiseAnd: + CvInvoke.BitwiseAnd(target, tempMat, target); + break; + case PixelArithmeticOperators.BitwiseOr: + CvInvoke.BitwiseOr(target, tempMat, target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.BitwiseXor: + CvInvoke.BitwiseXor(target, tempMat, target, _affectBackPixels ? null : target); + break; + case PixelArithmeticOperators.Threshold: + CvInvoke.Threshold(target, target, _value, _thresholdMaxValue, _thresholdType); + break; + case PixelArithmeticOperators.AbsDiff: + CvInvoke.AbsDiff(target, tempMat, target); + if (!_affectBackPixels) ApplyMask(original, target, original); + break; + case PixelArithmeticOperators.KeepRegion: + { + using var targetClone = target.Clone(); + original.SetTo(EmguExtensions.BlackByte); + mat.SetTo(EmguExtensions.BlackByte); + targetClone.CopyTo(target); + break; + } + case PixelArithmeticOperators.DiscardRegion: + target.SetTo(EmguExtensions.BlackByte); + break; + default: + throw new NotImplementedException(); + } + + ApplyMask(original, target); + + if (needDispose) + { + tempMat?.Dispose(); + } + + return true; + } + + public Mat GetTempMat() => _operator + is not PixelArithmeticOperators.BitwiseNot + and not PixelArithmeticOperators.KeepRegion + and not PixelArithmeticOperators.DiscardRegion ? EmguExtensions.InitMat(HaveROI ? ROI.Size : SlicerFile.Resolution, new MCvScalar(_value)) : null; + + public void PresetStripAntiAliasing() + { + Operator = PixelArithmeticOperators.Threshold; + Value = 127; + ThresholdMaxValue = 255; + ThresholdType = ThresholdType.Binary; + } + + public void PresetHalfBrightness() + { + Value = 128; + } + + #endregion + + #region Equality + + protected bool Equals(OperationPixelArithmetic other) + { + return _operator == other._operator && _value == other._value && _thresholdType == other._thresholdType && _thresholdMaxValue == other._thresholdMaxValue && _affectBackPixels == other._affectBackPixels; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((OperationPixelArithmetic) obj); + } + + public override int GetHashCode() + { + return HashCode.Combine((int) _operator, _value, (int) _thresholdType, _thresholdMaxValue, _affectBackPixels); + } + + #endregion + } +} |