// ============================================================================ // 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; /// /// 浮雕伪3D效果处理器(支持 8 位和 16 位灰度图像) /// 通过方向性卷积核模拟光照产生的凹凸立体感, /// 常用于X-ray图像的焊点、空洞等结构的可视化增强。 /// public class EmbossProcessor : ImageProcessorBase where TDepth : struct, IComparable { private static readonly ILogger _logger = Log.ForContext>(); 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")) { IsAdvanced = true }); Parameters.Add("GrayOffset", new ProcessorParameter( "GrayOffset", LocalizationHelper.GetString("EmbossProcessor_GrayOffset"), typeof(int), 128, 0, 255, LocalizationHelper.GetString("EmbossProcessor_GrayOffset_Desc")) { IsAdvanced = true }); _logger.Debug("InitializeParameters"); } public override Image Process(Image inputImage) { string direction = GetParameter("Direction"); double strength = GetParameter("Strength"); double blendRatio = GetParameter("BlendRatio"); int grayOffset = GetParameter("GrayOffset"); // 灰度偏移参数按 8 位语义定义,16 位时按比例放大 if (typeof(TDepth) == typeof(ushort)) grayOffset *= 256; grayOffset = Math.Clamp(grayOffset, 0, MaxPixelValue); // 获取方向性浮雕卷积核 float[,] kernelData = GetEmbossKernel(direction, strength); // 全程 float 运算,保留位深精度 var floatInput = inputImage.Convert(); // 应用浮雕卷积 using var kernel = new ConvolutionKernelF(kernelData); var embossed = new Image(inputImage.Size); CvInvoke.Filter2D(floatInput, embossed, kernel, new Point(-1, -1)); // 加灰度偏移,使平坦区域呈中灰色 var embossedWithOffset = embossed + grayOffset; Image resultFloat; if (blendRatio < 0.001) { // 纯浮雕 resultFloat = embossedWithOffset.Clone(); } else if (blendRatio > 0.999) { // 纯原图 resultFloat = floatInput.Clone(); } else { // Alpha 混合: result = original * blendRatio + embossed * (1 - blendRatio) // 浮雕分量先 clamp 到有效范围,模拟原 8 位实现中转字节的截断语义 var embossedClamped = PixelDepthHelper.FromFloatImageClamped(embossedWithOffset).Convert(); resultFloat = floatInput * blendRatio + embossedClamped * (1.0 - blendRatio); embossedClamped.Dispose(); } var result = PixelDepthHelper.FromFloatImageClamped(resultFloat); floatInput.Dispose(); embossed.Dispose(); embossedWithOffset.Dispose(); resultFloat.Dispose(); _logger.Debug("Process: Direction={Direction}, Strength={Strength}, BlendRatio={BlendRatio}, GrayOffset={GrayOffset}", direction, strength, blendRatio, grayOffset); return result; } /// /// 根据方向和强度生成 3x3 浮雕卷积核 /// 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 } } }; } }