// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件名: RotateProcessor.cs
// 描述: 图像旋转算子
// 功能:
// - 任意角度旋转
// - 支持保持原始尺寸或自适应扩展画布
// - 可选背景填充值
// - 支持双线性插值
// 算法: 仿射变换旋转
// 作者: 李伟 wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using XP.ImageProcessing.Core;
using Serilog;
using System.Drawing;
namespace XP.ImageProcessing.Processors;
///
/// 图像旋转算子(支持 8 位和 16 位灰度图像)
///
public class RotateProcessor : ImageProcessorBase
where TDepth : struct, IComparable
{
private static readonly ILogger _logger = Log.ForContext>();
public RotateProcessor()
{
Name = LocalizationHelper.GetString("RotateProcessor_Name");
Description = LocalizationHelper.GetString("RotateProcessor_Description");
}
protected override void InitializeParameters()
{
Parameters.Add("Angle", new ProcessorParameter(
"Angle",
LocalizationHelper.GetString("RotateProcessor_Angle"),
typeof(double),
90.0,
-360.0,
360.0,
LocalizationHelper.GetString("RotateProcessor_Angle_Desc")));
Parameters.Add("ExpandCanvas", new ProcessorParameter(
"ExpandCanvas",
LocalizationHelper.GetString("RotateProcessor_ExpandCanvas"),
typeof(bool),
false,
null,
null,
LocalizationHelper.GetString("RotateProcessor_ExpandCanvas_Desc")) { IsAdvanced = true });
Parameters.Add("BackgroundValue", new ProcessorParameter(
"BackgroundValue",
LocalizationHelper.GetString("RotateProcessor_BackgroundValue"),
typeof(int),
0,
0,
255,
LocalizationHelper.GetString("RotateProcessor_BackgroundValue_Desc")) { IsAdvanced = true });
Parameters.Add("Interpolation", new ProcessorParameter(
"Interpolation",
LocalizationHelper.GetString("RotateProcessor_Interpolation"),
typeof(string),
"Bilinear",
null,
null,
LocalizationHelper.GetString("RotateProcessor_Interpolation_Desc"),
new string[] { "Nearest", "Bilinear", "Bicubic" }) { IsAdvanced = true });
_logger.Debug("InitializeParameters");
}
public override Image Process(Image inputImage)
{
double angle = GetParameter("Angle");
bool expandCanvas = GetParameter("ExpandCanvas");
int bgValue = GetParameter("BackgroundValue");
string interpolation = GetParameter("Interpolation");
// 16 位时背景值按比例放大
if (typeof(TDepth) == typeof(ushort)) bgValue = bgValue * 256;
bgValue = Math.Clamp(bgValue, 0, MaxPixelValue);
Inter interMethod = interpolation switch
{
"Nearest" => Inter.Nearest,
"Bicubic" => Inter.Cubic,
_ => Inter.Linear
};
int srcW = inputImage.Width, srcH = inputImage.Height;
PointF center = new PointF(srcW / 2.0f, srcH / 2.0f);
using var rotMat = new Mat();
CvInvoke.GetRotationMatrix2D(center, angle, 1.0, rotMat);
int dstW, dstH;
if (expandCanvas)
{
double rad = Math.Abs(angle * Math.PI / 180.0);
double sinA = Math.Abs(Math.Sin(rad)), cosA = Math.Abs(Math.Cos(rad));
dstW = (int)Math.Ceiling(srcW * cosA + srcH * sinA);
dstH = (int)Math.Ceiling(srcW * sinA + srcH * cosA);
double[] m = new double[6];
rotMat.CopyTo(m);
m[2] += (dstW - srcW) / 2.0;
m[5] += (dstH - srcH) / 2.0;
using var adjusted = new Mat(2, 3, DepthType.Cv64F, 1);
System.Runtime.InteropServices.Marshal.Copy(m, 0, adjusted.DataPointer, 6);
var result = new Image(dstW, dstH);
result.SetValue(new Gray(bgValue));
CvInvoke.WarpAffine(inputImage, result, adjusted, new Size(dstW, dstH),
interMethod, Warp.Default, BorderType.Constant, new MCvScalar(bgValue));
_logger.Debug("Process: Angle={Angle}, ExpandCanvas=true, Size={W}x{H}", angle, dstW, dstH);
return result;
}
else
{
var result = new Image(srcW, srcH);
result.SetValue(new Gray(bgValue));
CvInvoke.WarpAffine(inputImage, result, rotMat, new Size(srcW, srcH),
interMethod, Warp.Default, BorderType.Constant, new MCvScalar(bgValue));
_logger.Debug("Process: Angle={Angle}, ExpandCanvas=false", angle);
return result;
}
}
}