197 lines
6.8 KiB
C#
197 lines
6.8 KiB
C#
// ============================================================================
|
|
// 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;
|
|
|
|
/// <summary>
|
|
/// 电子胶片效果算子
|
|
/// </summary>
|
|
public class FilmEffectProcessor : ImageProcessorBase
|
|
{
|
|
private static readonly ILogger _logger = Log.ForContext<FilmEffectProcessor>();
|
|
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<Gray, byte> Process(Image<Gray, byte> inputImage)
|
|
{
|
|
int windowCenter = GetParameter<int>("WindowCenter");
|
|
int windowWidth = GetParameter<int>("WindowWidth");
|
|
bool invert = GetParameter<bool>("Invert");
|
|
string curve = GetParameter<string>("Curve");
|
|
double curveStrength = GetParameter<double>("CurveStrength");
|
|
double edgeEnhance = GetParameter<double>("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<Gray, float>(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);
|
|
}
|
|
}
|
|
|
|
/// <summary>S曲线(Sigmoid):增强中间调对比度</summary>
|
|
private static double ApplySigmoid(double x, double strength)
|
|
{
|
|
double k = strength * 10.0;
|
|
return 1.0 / (1.0 + Math.Exp(-k * (x - 0.5)));
|
|
}
|
|
|
|
/// <summary>对数曲线:提亮暗部,压缩亮部</summary>
|
|
private static double ApplyLogarithmic(double x, double strength)
|
|
{
|
|
double c = strength;
|
|
return Math.Log(1.0 + c * x) / Math.Log(1.0 + c);
|
|
}
|
|
|
|
/// <summary>指数曲线:压缩暗部,增强亮部</summary>
|
|
private static double ApplyExponential(double x, double strength)
|
|
{
|
|
double c = strength;
|
|
return (Math.Exp(c * x) - 1.0) / (Math.Exp(c) - 1.0);
|
|
}
|
|
} |