// ============================================================================ // Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved. // 文件名: FilmEffectProcessor.cs // 描述: 电子胶片效果算子,模拟传统X射线胶片的显示效果 // 功能: // - 窗宽窗位(Window/Level)调整 // - 胶片反转(正片/负片) // - 多种胶片特性曲线(线性、S曲线、对数、指数) // - 边缘增强(模拟胶片锐化效果) // - 使用查找表(LUT)加速处理 // 算法: 窗宽窗位映射 + 特性曲线变换 // 作者: 李伟 wei.lw.li@hexagon.com // ============================================================================ using Emgu.CV; using Emgu.CV.CvEnum; using Emgu.CV.Structure; using XP.ImageProcessing.Core; using Serilog; namespace XP.ImageProcessing.Processors; /// /// 电子胶片效果算子 /// public class FilmEffectProcessor : ImageProcessorBase { private static readonly ILogger _logger = Log.ForContext(); private byte[] _lut = new byte[256]; public FilmEffectProcessor() { Name = LocalizationHelper.GetString("FilmEffectProcessor_Name"); Description = LocalizationHelper.GetString("FilmEffectProcessor_Description"); } protected override void InitializeParameters() { Parameters.Add("WindowCenter", new ProcessorParameter( "WindowCenter", LocalizationHelper.GetString("FilmEffectProcessor_WindowCenter"), typeof(int), 128, 0, 255, LocalizationHelper.GetString("FilmEffectProcessor_WindowCenter_Desc"))); Parameters.Add("WindowWidth", new ProcessorParameter( "WindowWidth", LocalizationHelper.GetString("FilmEffectProcessor_WindowWidth"), typeof(int), 255, 1, 255, LocalizationHelper.GetString("FilmEffectProcessor_WindowWidth_Desc"))); Parameters.Add("Invert", new ProcessorParameter( "Invert", LocalizationHelper.GetString("FilmEffectProcessor_Invert"), typeof(bool), false, null, null, LocalizationHelper.GetString("FilmEffectProcessor_Invert_Desc"))); Parameters.Add("Curve", new ProcessorParameter( "Curve", LocalizationHelper.GetString("FilmEffectProcessor_Curve"), typeof(string), "Linear", null, null, LocalizationHelper.GetString("FilmEffectProcessor_Curve_Desc"), new string[] { "Linear", "Sigmoid", "Logarithmic", "Exponential" })); Parameters.Add("CurveStrength", new ProcessorParameter( "CurveStrength", LocalizationHelper.GetString("FilmEffectProcessor_CurveStrength"), typeof(double), 1.0, 0.1, 5.0, LocalizationHelper.GetString("FilmEffectProcessor_CurveStrength_Desc"))); Parameters.Add("EdgeEnhance", new ProcessorParameter( "EdgeEnhance", LocalizationHelper.GetString("FilmEffectProcessor_EdgeEnhance"), typeof(double), 0.0, 0.0, 3.0, LocalizationHelper.GetString("FilmEffectProcessor_EdgeEnhance_Desc"))); _logger.Debug("InitializeParameters"); } public override Image Process(Image inputImage) { int windowCenter = GetParameter("WindowCenter"); int windowWidth = GetParameter("WindowWidth"); bool invert = GetParameter("Invert"); string curve = GetParameter("Curve"); double curveStrength = GetParameter("CurveStrength"); double edgeEnhance = GetParameter("EdgeEnhance"); // 构建查找表 BuildLUT(windowCenter, windowWidth, invert, curve, curveStrength); // 应用 LUT var result = inputImage.Clone(); int width = result.Width; int height = result.Height; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { result.Data[y, x, 0] = _lut[result.Data[y, x, 0]]; } } // 边缘增强(模拟胶片锐化) if (edgeEnhance > 0.01) { using var blurred = inputImage.SmoothGaussian(3); using var detail = new Image(width, height); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { float diff = inputImage.Data[y, x, 0] - blurred.Data[y, x, 0]; float enhanced = result.Data[y, x, 0] + (float)(diff * edgeEnhance); result.Data[y, x, 0] = (byte)Math.Clamp((int)enhanced, 0, 255); } } } _logger.Debug("Process: WC={WC}, WW={WW}, Invert={Inv}, Curve={Curve}, Strength={Str}, Edge={Edge}", windowCenter, windowWidth, invert, curve, curveStrength, edgeEnhance); return result; } private void BuildLUT(int wc, int ww, bool invert, string curve, double strength) { double halfW = ww / 2.0; double low = wc - halfW; double high = wc + halfW; for (int i = 0; i < 256; i++) { // 窗宽窗位映射到 [0, 1] double normalized; if (ww <= 1) normalized = i >= wc ? 1.0 : 0.0; else normalized = Math.Clamp((i - low) / (high - low), 0.0, 1.0); // 应用特性曲线 double mapped = curve switch { "Sigmoid" => ApplySigmoid(normalized, strength), "Logarithmic" => ApplyLogarithmic(normalized, strength), "Exponential" => ApplyExponential(normalized, strength), _ => normalized // Linear }; // 反转(负片效果) if (invert) mapped = 1.0 - mapped; _lut[i] = (byte)Math.Clamp((int)(mapped * 255.0), 0, 255); } } /// S曲线(Sigmoid):增强中间调对比度 private static double ApplySigmoid(double x, double strength) { double k = strength * 10.0; return 1.0 / (1.0 + Math.Exp(-k * (x - 0.5))); } /// 对数曲线:提亮暗部,压缩亮部 private static double ApplyLogarithmic(double x, double strength) { double c = strength; return Math.Log(1.0 + c * x) / Math.Log(1.0 + c); } /// 指数曲线:压缩暗部,增强亮部 private static double ApplyExponential(double x, double strength) { double c = strength; return (Math.Exp(c * x) - 1.0) / (Math.Exp(c) - 1.0); } }