Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'UVtools.Core/Extensions/EmguExtensions.cs')
-rw-r--r--UVtools.Core/Extensions/EmguExtensions.cs175
1 files changed, 161 insertions, 14 deletions
diff --git a/UVtools.Core/Extensions/EmguExtensions.cs b/UVtools.Core/Extensions/EmguExtensions.cs
index 3a55047..4240372 100644
--- a/UVtools.Core/Extensions/EmguExtensions.cs
+++ b/UVtools.Core/Extensions/EmguExtensions.cs
@@ -8,23 +8,20 @@
using System;
using System.Drawing;
-using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace UVtools.Core.Extensions
{
public static class EmguExtensions
{
- public static readonly MCvScalar WhiteByte = new(255);
- public static readonly MCvScalar White3Byte = new(255, 255, 255);
- public static readonly MCvScalar BlackByte = new(0);
- public static readonly MCvScalar Black3Byte = new(0, 0, 0);
- public static readonly MCvScalar Transparent4Byte = new(0, 0, 0, 0);
- public static readonly MCvScalar Black4Byte = new(0, 0, 0, 255);
+ public static readonly MCvScalar WhiteColor = new(255, 255, 255, 255);
+ public static readonly MCvScalar BlackColor = new(0, 0, 0, 255);
+ //public static readonly MCvScalar TransparentColor = new();
public static unsafe byte* GetBytePointer(this Mat mat)
{
@@ -72,7 +69,7 @@ namespace UVtools.Core.Extensions
public static unsafe Span<T> GetPixelColSpan<T>(this Mat mat, int x, int length = 0, int offset = 0)
{
var colMat = mat.Col(x);
- return new(IntPtr.Add(mat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length);
+ return new(IntPtr.Add(colMat.DataPointer, offset).ToPointer(), length <= 0 ? mat.Height : length);
}
/// <summary>
@@ -95,25 +92,89 @@ namespace UVtools.Core.Extensions
public static void Transform(this Mat src, double xScale, double yScale, double xTrans = 0, double yTrans = 0, Size dstSize = default, Inter interpolation = Inter.Linear)
{
//var dst = new Mat(src.Size, src.Depth, src.NumberOfChannels);
- using (var translateTransform = new Matrix<double>(2, 3)
+ using var translateTransform = new Matrix<double>(2, 3)
{
[0, 0] = xScale, // xScale
[1, 1] = yScale, // yScale
[0, 2] = xTrans, //x translation + compensation of x scaling
[1, 2] = yTrans // y translation + compensation of y scaling
- })
+ };
+ CvInvoke.WarpAffine(src, src, translateTransform, dstSize.IsEmpty ? src.Size : dstSize, interpolation);
+ }
+
+ /// <summary>
+ /// Rotates a Mat by an angle while keeping the image size
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="angle"></param>
+ /// <param name="scale"></param>
+ public static void Rotate(this Mat src, double angle, Size newSize = default, double scale = 1.0) => Rotate(src, src, angle, newSize, scale);
+
+ /// <summary>
+ /// Rotates a Mat by an angle while keeping the image size
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="dst"></param>
+ /// <param name="angle"></param>
+ /// <param name="scale"></param>
+ public static void Rotate(this Mat src, Mat dst, double angle, Size newSize = default, double scale = 1.0)
+ {
+ if (angle % 360 == 0 && scale == 1.0) return;
+ if (newSize.IsEmpty)
+ {
+ newSize = src.Size;
+ }
+
+ var halfWidth = src.Width / 2.0f;
+ var halfHeight = src.Height / 2.0f;
+ using var translateTransform = new Matrix<double>(2, 3);
+ CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, scale, translateTransform);
+
+ if (src.Size != newSize)
{
- CvInvoke.WarpAffine(src, src, translateTransform, dstSize.IsEmpty ? src.Size : dstSize, interpolation);
+ // adjust the rotation matrix to take into account translation
+ translateTransform[0, 2] += newSize.Width / 2.0 - halfWidth;
+ translateTransform[1, 2] += newSize.Height / 2.0 - halfHeight;
}
+
+ CvInvoke.WarpAffine(src, dst, translateTransform, newSize);
}
- public static void Rotate(this Mat src, double angle)
+ /// <summary>
+ /// Rotates a Mat by an angle while adjusting bounds to fit the rotated content
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="angle"></param>
+ /// <param name="scale"></param>
+ public static void RotateAdjustBounds(this Mat src, double angle, double scale = 1.0) => RotateAdjustBounds(src, src, angle, scale);
+
+ /// <summary>
+ /// Rotates a Mat by an angle while adjusting bounds to fit the rotated content
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="dst"></param>
+ /// <param name="angle"></param>
+ /// <param name="scale"></param>
+ public static void RotateAdjustBounds(this Mat src, Mat dst, double angle, double scale = 1.0)
{
+ if (angle % 360 == 0 && scale == 1.0) return;
var halfWidth = src.Width / 2.0f;
var halfHeight = src.Height / 2.0f;
using var translateTransform = new Matrix<double>(2, 3);
- CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, 1.0, translateTransform);
- CvInvoke.WarpAffine(src, src, translateTransform, src.Size);
+ CvInvoke.GetRotationMatrix2D(new PointF(halfWidth, halfHeight), -angle, scale, translateTransform);
+ var cos = Math.Abs(translateTransform[0, 0]);
+ var sin = Math.Abs(translateTransform[0, 1]);
+
+ // compute the new bounding dimensions of the image
+ int newWidth = (int) (src.Height * sin + src.Width * cos);
+ int newHeight = (int) (src.Height * cos + src.Width * sin);
+
+ // adjust the rotation matrix to take into account translation
+ translateTransform[0, 2] += newWidth / 2.0 - halfWidth;
+ translateTransform[1, 2] += newHeight / 2.0 - halfHeight;
+
+
+ CvInvoke.WarpAffine(src, dst, translateTransform, new Size(newWidth, newHeight));
}
/// <summary>
@@ -134,6 +195,85 @@ namespace UVtools.Core.Extensions
yTrans + (src.Height - src.Height * yScale) / 2.0, dstSize, interpolation);
}
+ public static void PutTextRotated(this Mat src, string text, Point org, FontFace fontFace, double fontScale, MCvScalar color,
+ int thickness = 1, LineType lineType = LineType.EightConnected, bool bottomLeftOrigin = false, double angle = 0)
+ {
+ if (angle % 360 == 0) // No rotation needed, cheaper cycle
+ {
+ CvInvoke.PutText(src, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
+ return;
+ }
+
+ using var rotatedSrc = src.Clone();
+ rotatedSrc.RotateAdjustBounds(-angle);
+ var sizeDifference = (rotatedSrc.Size - src.Size).Half();
+ org.Offset(sizeDifference.ToPoint());
+ org = org.Rotate(-angle, new Point(rotatedSrc.Size.Width / 2, rotatedSrc.Size.Height / 2));
+ CvInvoke.PutText(rotatedSrc, text, org, fontFace, fontScale, color, thickness, lineType, bottomLeftOrigin);
+
+ using var mask = rotatedSrc.CloneBlank();
+ CvInvoke.PutText(mask, text, org, fontFace, fontScale, WhiteColor, thickness, lineType, bottomLeftOrigin);
+
+ rotatedSrc.Rotate(angle, src.Size);
+ mask.Rotate(angle, src.Size);
+
+ rotatedSrc.CopyTo(src, mask);
+ }
+
+ /// <summary>
+ /// Determine the area (i.e. total number of pixels in the image),
+ /// initialize the output skeletonized image, and construct the
+ /// morphological structuring element
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="iterations">Number of iterations required to perform the skeletoize</param>
+ /// <param name="ksize"></param>
+ /// <param name="elementShape"></param>
+ public static Mat Skeletonize(this Mat src, out int iterations, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle)
+ {
+ if (ksize.IsEmpty) ksize = new Size(3, 3);
+ Point anchor = new(-1, -1);
+ var skeleton = src.CloneBlank();
+ using var kernel = CvInvoke.GetStructuringElement(elementShape, ksize, anchor);
+
+ var image = src;
+ using var temp = new Mat();
+ iterations = 0;
+ while (true)
+ {
+ iterations++;
+
+ // erode and dilate the image using the structuring element
+ using var eroded = new Mat();
+ CvInvoke.Erode(image, eroded, kernel, anchor, 1, BorderType.Reflect101, default);
+ CvInvoke.Dilate(eroded, temp, kernel, anchor, 1, BorderType.Reflect101, default);
+
+ // subtract the temporary image from the original, eroded
+ // image, then take the bitwise 'or' between the skeleton
+ // and the temporary image
+ CvInvoke.Subtract(image, temp, temp);
+ CvInvoke.BitwiseOr(skeleton, temp, skeleton);
+ image = eroded.Clone();
+
+ // if there are no more 'white' pixels in the image, then
+ // break from the loop
+ if (CvInvoke.CountNonZero(image) == 0) break;
+ }
+
+ return skeleton;
+ }
+
+ /// <summary>
+ /// Determine the area (i.e. total number of pixels in the image),
+ /// initialize the output skeletonized image, and construct the
+ /// morphological structuring element
+ /// </summary>
+ /// <param name="src"></param>
+ /// <param name="ksize"></param>
+ /// <param name="elementShape"></param>
+ public static Mat Skeletonize(this Mat src, Size ksize = default, ElementShape elementShape = ElementShape.Rectangle)
+ => src.Skeletonize(out _, ksize, elementShape);
+
/// <summary>
/// Copy a <see cref="Mat"/> to center of other <see cref="Mat"/>
/// </summary>
@@ -226,6 +366,13 @@ namespace UVtools.Core.Extensions
return new(mat.Rows, mat.Cols, mat.Depth, mat.NumberOfChannels);
}
+ public static Mat New(this Mat src, MCvScalar color)
+ {
+ Mat mat = new(src.Rows, src.Cols, src.Depth, src.NumberOfChannels);
+ mat.SetTo(color);
+ return mat;
+ }
+
/// <summary>
/// Clone this <see cref="Mat"/> blanked (All zeros)
/// </summary>