Files
2026-04-20 09:58:35 +08:00

207 lines
6.9 KiB
C#

// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件名: EmbossProcessor.cs
// 描述: 浮雕伪3D效果处理器,模拟Viscom X-ray检测软件中的浮雕显示效果
// 功能:
// - 方向性浮雕(8个方向可选)
// - 可调节浮雕深度(强度)
// - 可选灰度偏移(中灰基底)
// - 支持与原图混合,实现伪3D立体感
// 算法: 方向性卷积核 + 灰度偏移 + Alpha混合
// 作者: 李伟 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;
/// <summary>
/// 浮雕伪3D效果处理器
/// 通过方向性卷积核模拟光照产生的凹凸立体感,
/// 常用于X-ray图像的焊点、空洞等结构的可视化增强。
/// </summary>
public class EmbossProcessor : ImageProcessorBase
{
private static readonly ILogger _logger = Log.ForContext<EmbossProcessor>();
public EmbossProcessor()
{
Name = LocalizationHelper.GetString("EmbossProcessor_Name");
Description = LocalizationHelper.GetString("EmbossProcessor_Description");
}
protected override void InitializeParameters()
{
Parameters.Add("Direction", new ProcessorParameter(
"Direction",
LocalizationHelper.GetString("EmbossProcessor_Direction"),
typeof(string),
"TopLeft",
null,
null,
LocalizationHelper.GetString("EmbossProcessor_Direction_Desc"),
new string[] { "TopLeft", "Top", "TopRight", "Left", "Right", "BottomLeft", "Bottom", "BottomRight" }));
Parameters.Add("Strength", new ProcessorParameter(
"Strength",
LocalizationHelper.GetString("EmbossProcessor_Strength"),
typeof(double),
1.0,
0.1,
5.0,
LocalizationHelper.GetString("EmbossProcessor_Strength_Desc")));
Parameters.Add("BlendRatio", new ProcessorParameter(
"BlendRatio",
LocalizationHelper.GetString("EmbossProcessor_BlendRatio"),
typeof(double),
0.5,
0.0,
1.0,
LocalizationHelper.GetString("EmbossProcessor_BlendRatio_Desc")));
Parameters.Add("GrayOffset", new ProcessorParameter(
"GrayOffset",
LocalizationHelper.GetString("EmbossProcessor_GrayOffset"),
typeof(int),
128,
0,
255,
LocalizationHelper.GetString("EmbossProcessor_GrayOffset_Desc")));
_logger.Debug("InitializeParameters");
}
public override Image<Gray, byte> Process(Image<Gray, byte> inputImage)
{
string direction = GetParameter<string>("Direction");
double strength = GetParameter<double>("Strength");
double blendRatio = GetParameter<double>("BlendRatio");
int grayOffset = GetParameter<int>("GrayOffset");
// 获取方向性浮雕卷积核
float[,] kernelData = GetEmbossKernel(direction, strength);
// 应用浮雕卷积
var kernel = new ConvolutionKernelF(kernelData);
var embossed = new Image<Gray, float>(inputImage.Size);
CvInvoke.Filter2D(inputImage, embossed, kernel, new Point(-1, -1));
// 加灰度偏移,使平坦区域呈中灰色
var offset = new Image<Gray, float>(inputImage.Size);
offset.SetValue(new Gray(grayOffset));
var embossedWithOffset = embossed + offset;
// 裁剪到 [0, 255] 并转为字节
var embossedByte = embossedWithOffset.Convert<Gray, byte>();
// 与原图混合实现伪3D效果
Image<Gray, byte> result;
if (blendRatio < 0.001)
{
// 纯浮雕
result = embossedByte;
}
else if (blendRatio > 0.999)
{
// 纯原图
embossedByte.Dispose();
result = inputImage.Clone();
}
else
{
// Alpha 混合: result = original * blendRatio + embossed * (1 - blendRatio)
var floatOriginal = inputImage.Convert<Gray, float>();
var floatEmbossed = embossedByte.Convert<Gray, float>();
var blended = floatOriginal * blendRatio + floatEmbossed * (1.0 - blendRatio);
result = blended.Convert<Gray, byte>();
floatOriginal.Dispose();
floatEmbossed.Dispose();
blended.Dispose();
embossedByte.Dispose();
}
// 清理
kernel.Dispose();
embossed.Dispose();
offset.Dispose();
embossedWithOffset.Dispose();
_logger.Debug("Process: Direction={Direction}, Strength={Strength}, BlendRatio={BlendRatio}, GrayOffset={GrayOffset}",
direction, strength, blendRatio, grayOffset);
return result;
}
/// <summary>
/// 根据方向和强度生成 3x3 浮雕卷积核
/// </summary>
private static float[,] GetEmbossKernel(string direction, double strength)
{
float s = (float)strength;
return direction switch
{
"TopLeft" => new float[,]
{
{ -2 * s, -1 * s, 0 },
{ -1 * s, 1, 1 * s },
{ 0, 1 * s, 2 * s }
},
"Top" => new float[,]
{
{ -1 * s, -1 * s, -1 * s },
{ 0, 1, 0 },
{ 1 * s, 1 * s, 1 * s }
},
"TopRight" => new float[,]
{
{ 0, -1 * s, -2 * s },
{ 1 * s, 1, -1 * s },
{ 2 * s, 1 * s, 0 }
},
"Left" => new float[,]
{
{ -1 * s, 0, 1 * s },
{ -1 * s, 1, 1 * s },
{ -1 * s, 0, 1 * s }
},
"Right" => new float[,]
{
{ 1 * s, 0, -1 * s },
{ 1 * s, 1, -1 * s },
{ 1 * s, 0, -1 * s }
},
"BottomLeft" => new float[,]
{
{ 0, 1 * s, 2 * s },
{ -1 * s, 1, 1 * s },
{ -2 * s, -1 * s, 0 }
},
"Bottom" => new float[,]
{
{ 1 * s, 1 * s, 1 * s },
{ 0, 1, 0 },
{ -1 * s, -1 * s, -1 * s }
},
"BottomRight" => new float[,]
{
{ 2 * s, 1 * s, 0 },
{ 1 * s, 1, -1 * s },
{ 0, -1 * s, -2 * s }
},
_ => new float[,]
{
{ -2 * s, -1 * s, 0 },
{ -1 * s, 1, 1 * s },
{ 0, 1 * s, 2 * s }
}
};
}
}