规范类名及命名空间名称
This commit is contained in:
@@ -0,0 +1,549 @@
|
||||
// ============================================================================
|
||||
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
|
||||
// 文件� HDREnhancementProcessor.cs
|
||||
// æ��è¿°: 高动æ€�范围(HDR)图åƒ�增强算å?
|
||||
// 功能:
|
||||
// - å±€éƒ¨è‰²è°ƒæ˜ å°„ï¼ˆLocal Tone Mappingï¼?
|
||||
// - è‡ªé€‚åº”å¯¹æ•°æ˜ å°„ï¼ˆAdaptive Logarithmic Mappingï¼?
|
||||
// - Dragoè‰²è°ƒæ˜ å°„
|
||||
// - å�Œè¾¹æ»¤æ³¢è‰²è°ƒæ˜ å°„
|
||||
// - 增强图�暗部和亮部细�
|
||||
// 算法: åŸºäºŽè‰²è°ƒæ˜ å°„çš„HDR增强
|
||||
// 作� �伟 wei.lw.li@hexagon.com
|
||||
// ============================================================================
|
||||
|
||||
using Emgu.CV;
|
||||
using Emgu.CV.Structure;
|
||||
using Serilog;
|
||||
using XP.ImageProcessing.Core;
|
||||
|
||||
namespace XP.ImageProcessing.Processors;
|
||||
|
||||
/// <summary>
|
||||
/// 高动æ€�范围图åƒ�增强算å?
|
||||
/// </summary>
|
||||
public class HDREnhancementProcessor : ImageProcessorBase
|
||||
{
|
||||
private static readonly ILogger _logger = Log.ForContext<HDREnhancementProcessor>();
|
||||
|
||||
public HDREnhancementProcessor()
|
||||
{
|
||||
Name = LocalizationHelper.GetString("HDREnhancementProcessor_Name");
|
||||
Description = LocalizationHelper.GetString("HDREnhancementProcessor_Description");
|
||||
}
|
||||
|
||||
protected override void InitializeParameters()
|
||||
{
|
||||
Parameters.Add("Method", new ProcessorParameter(
|
||||
"Method",
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_Method"),
|
||||
typeof(string),
|
||||
"LocalToneMap",
|
||||
null,
|
||||
null,
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_Method_Desc"),
|
||||
new string[] { "LocalToneMap", "AdaptiveLog", "Drago", "BilateralToneMap" }));
|
||||
|
||||
Parameters.Add("Gamma", new ProcessorParameter(
|
||||
"Gamma",
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_Gamma"),
|
||||
typeof(double),
|
||||
1.0,
|
||||
0.1,
|
||||
5.0,
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_Gamma_Desc")));
|
||||
|
||||
Parameters.Add("Saturation", new ProcessorParameter(
|
||||
"Saturation",
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_Saturation"),
|
||||
typeof(double),
|
||||
1.0,
|
||||
0.0,
|
||||
3.0,
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_Saturation_Desc")));
|
||||
|
||||
Parameters.Add("DetailBoost", new ProcessorParameter(
|
||||
"DetailBoost",
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_DetailBoost"),
|
||||
typeof(double),
|
||||
1.5,
|
||||
0.0,
|
||||
5.0,
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_DetailBoost_Desc")));
|
||||
|
||||
Parameters.Add("SigmaSpace", new ProcessorParameter(
|
||||
"SigmaSpace",
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_SigmaSpace"),
|
||||
typeof(double),
|
||||
20.0,
|
||||
1.0,
|
||||
100.0,
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_SigmaSpace_Desc")));
|
||||
|
||||
Parameters.Add("SigmaColor", new ProcessorParameter(
|
||||
"SigmaColor",
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_SigmaColor"),
|
||||
typeof(double),
|
||||
30.0,
|
||||
1.0,
|
||||
100.0,
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_SigmaColor_Desc")));
|
||||
|
||||
Parameters.Add("Bias", new ProcessorParameter(
|
||||
"Bias",
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_Bias"),
|
||||
typeof(double),
|
||||
0.85,
|
||||
0.0,
|
||||
1.0,
|
||||
LocalizationHelper.GetString("HDREnhancementProcessor_Bias_Desc")));
|
||||
|
||||
_logger.Debug("InitializeParameters");
|
||||
}
|
||||
|
||||
public override Image<Gray, byte> Process(Image<Gray, byte> inputImage)
|
||||
{
|
||||
string method = GetParameter<string>("Method");
|
||||
double gamma = GetParameter<double>("Gamma");
|
||||
double saturation = GetParameter<double>("Saturation");
|
||||
double detailBoost = GetParameter<double>("DetailBoost");
|
||||
double sigmaSpace = GetParameter<double>("SigmaSpace");
|
||||
double sigmaColor = GetParameter<double>("SigmaColor");
|
||||
double bias = GetParameter<double>("Bias");
|
||||
|
||||
Image<Gray, byte> result;
|
||||
|
||||
switch (method)
|
||||
{
|
||||
case "AdaptiveLog":
|
||||
result = AdaptiveLogarithmicMapping(inputImage, gamma, bias);
|
||||
break;
|
||||
|
||||
case "Drago":
|
||||
result = DragoToneMapping(inputImage, gamma, bias);
|
||||
break;
|
||||
|
||||
case "BilateralToneMap":
|
||||
result = BilateralToneMapping(inputImage, gamma, sigmaSpace, sigmaColor, detailBoost);
|
||||
break;
|
||||
|
||||
default: // LocalToneMap
|
||||
result = LocalToneMapping(inputImage, gamma, sigmaSpace, detailBoost, saturation);
|
||||
break;
|
||||
}
|
||||
|
||||
_logger.Debug("Process: Method={Method}, Gamma={Gamma}, Saturation={Saturation}, DetailBoost={DetailBoost}, SigmaSpace={SigmaSpace}, SigmaColor={SigmaColor}, Bias={Bias}",
|
||||
method, gamma, saturation, detailBoost, sigmaSpace, sigmaColor, bias);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// å±€éƒ¨è‰²è°ƒæ˜ å°?
|
||||
/// 将图�分解为基础层(光照)和细节层,分别处����
|
||||
/// Base = GaussianBlur(log(I))
|
||||
/// Detail = log(I) - Base
|
||||
/// Output = exp(Base_compressed + Detail * boost)
|
||||
/// </summary>
|
||||
private Image<Gray, byte> LocalToneMapping(Image<Gray, byte> inputImage,
|
||||
double gamma, double sigmaSpace, double detailBoost, double saturation)
|
||||
{
|
||||
int width = inputImage.Width;
|
||||
int height = inputImage.Height;
|
||||
|
||||
// 转�为浮点并归一化到 (0, 1]
|
||||
var floatImage = inputImage.Convert<Gray, float>();
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
floatImage.Data[y, x, 0] = floatImage.Data[y, x, 0] / 255.0f + 0.001f;
|
||||
|
||||
// 对数�
|
||||
var logImage = new Image<Gray, float>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
logImage.Data[y, x, 0] = (float)Math.Log(floatImage.Data[y, x, 0]);
|
||||
|
||||
// 基础层:大尺度高斯模糊��光照分�
|
||||
int kernelSize = (int)(sigmaSpace * 6) | 1;
|
||||
if (kernelSize < 3) kernelSize = 3;
|
||||
var baseLayer = new Image<Gray, float>(width, height);
|
||||
CvInvoke.GaussianBlur(logImage, baseLayer, new System.Drawing.Size(kernelSize, kernelSize), sigmaSpace);
|
||||
|
||||
// 细节�
|
||||
var detailLayer = logImage - baseLayer;
|
||||
|
||||
// 压缩基础层的动�范�
|
||||
double baseMin = double.MaxValue, baseMax = double.MinValue;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float v = baseLayer.Data[y, x, 0];
|
||||
if (v < baseMin) baseMin = v;
|
||||
if (v > baseMax) baseMax = v;
|
||||
}
|
||||
}
|
||||
|
||||
double baseRange = baseMax - baseMin;
|
||||
if (baseRange < 0.001) baseRange = 0.001;
|
||||
|
||||
// ç›®æ ‡åŠ¨æ€�范围(对数域)
|
||||
double targetRange = Math.Log(256.0);
|
||||
double compressionFactor = targetRange / baseRange;
|
||||
|
||||
var compressedBase = new Image<Gray, float>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float normalized = (float)((baseLayer.Data[y, x, 0] - baseMin) / baseRange);
|
||||
compressedBase.Data[y, x, 0] = (float)(normalized * targetRange + Math.Log(0.01));
|
||||
}
|
||||
}
|
||||
|
||||
// ��:压缩�的基础�+ 增强的细节层
|
||||
var combined = new Image<Gray, float>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float val = compressedBase.Data[y, x, 0] + detailLayer.Data[y, x, 0] * (float)detailBoost;
|
||||
combined.Data[y, x, 0] = val;
|
||||
}
|
||||
}
|
||||
|
||||
// 指数��回线性域
|
||||
var linearResult = new Image<Gray, float>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
linearResult.Data[y, x, 0] = (float)Math.Exp(combined.Data[y, x, 0]);
|
||||
|
||||
// Gammaæ ¡æ£
|
||||
if (Math.Abs(gamma - 1.0) > 0.01)
|
||||
{
|
||||
double invGamma = 1.0 / gamma;
|
||||
double maxVal = 0;
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
if (linearResult.Data[y, x, 0] > maxVal) maxVal = linearResult.Data[y, x, 0];
|
||||
|
||||
if (maxVal > 0)
|
||||
{
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
double normalized = linearResult.Data[y, x, 0] / maxVal;
|
||||
linearResult.Data[y, x, 0] = (float)(Math.Pow(normalized, invGamma) * maxVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 饱和度增强(对比度微调)
|
||||
if (Math.Abs(saturation - 1.0) > 0.01)
|
||||
{
|
||||
double mean = 0;
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
mean += linearResult.Data[y, x, 0];
|
||||
mean /= (width * height);
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
double diff = linearResult.Data[y, x, 0] - mean;
|
||||
linearResult.Data[y, x, 0] = (float)(mean + diff * saturation);
|
||||
}
|
||||
}
|
||||
|
||||
// 归一化到 [0, 255]
|
||||
var result = NormalizeToByteImage(linearResult);
|
||||
|
||||
floatImage.Dispose();
|
||||
logImage.Dispose();
|
||||
baseLayer.Dispose();
|
||||
detailLayer.Dispose();
|
||||
compressedBase.Dispose();
|
||||
combined.Dispose();
|
||||
linearResult.Dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// è‡ªé€‚åº”å¯¹æ•°æ˜ å°„
|
||||
/// æ ¹æ�®åœºæ™¯çš„æ•´ä½“äº®åº¦è‡ªé€‚åº”è°ƒæ•´å¯¹æ•°æ˜ å°„æ›²çº¿
|
||||
/// L_out = (log(1 + L_in) / log(1 + L_max)) ^ (1/gamma)
|
||||
/// 使用局部自适应:L_max æ ¹æ�®é‚»åŸŸè®¡ç®—
|
||||
/// </summary>
|
||||
private Image<Gray, byte> AdaptiveLogarithmicMapping(Image<Gray, byte> inputImage,
|
||||
double gamma, double bias)
|
||||
{
|
||||
int width = inputImage.Width;
|
||||
int height = inputImage.Height;
|
||||
|
||||
var floatImage = inputImage.Convert<Gray, float>();
|
||||
|
||||
// 归一化到 [0, 1]
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
floatImage.Data[y, x, 0] /= 255.0f;
|
||||
|
||||
// 计算全局最大亮�
|
||||
float globalMax = 0;
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
if (floatImage.Data[y, x, 0] > globalMax)
|
||||
globalMax = floatImage.Data[y, x, 0];
|
||||
|
||||
if (globalMax < 0.001f) globalMax = 0.001f;
|
||||
|
||||
// 计算对数平�亮度
|
||||
double logAvg = 0;
|
||||
int count = 0;
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float v = floatImage.Data[y, x, 0];
|
||||
if (v > 0.001f)
|
||||
{
|
||||
logAvg += Math.Log(v);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
logAvg = Math.Exp(logAvg / Math.Max(count, 1));
|
||||
|
||||
// è‡ªé€‚åº”å¯¹æ•°æ˜ å°„
|
||||
// bias 控制暗部和亮部的平衡
|
||||
double logBase = Math.Log(2.0 + 8.0 * Math.Pow(logAvg / globalMax, Math.Log(bias) / Math.Log(0.5)));
|
||||
|
||||
var result = new Image<Gray, float>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float lum = floatImage.Data[y, x, 0];
|
||||
double mapped = Math.Log(1.0 + lum) / logBase;
|
||||
result.Data[y, x, 0] = (float)mapped;
|
||||
}
|
||||
}
|
||||
|
||||
// Gammaæ ¡æ£
|
||||
if (Math.Abs(gamma - 1.0) > 0.01)
|
||||
{
|
||||
double invGamma = 1.0 / gamma;
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
result.Data[y, x, 0] = (float)Math.Pow(Math.Max(0, result.Data[y, x, 0]), invGamma);
|
||||
}
|
||||
|
||||
var byteResult = NormalizeToByteImage(result);
|
||||
|
||||
floatImage.Dispose();
|
||||
result.Dispose();
|
||||
|
||||
return byteResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Dragoè‰²è°ƒæ˜ å°„
|
||||
/// ä½¿ç”¨è‡ªé€‚åº”å¯¹æ•°åŸºåº•è¿›è¡Œè‰²è°ƒæ˜ å°„
|
||||
/// L_out = log_base(1 + L_in) / log_base(1 + L_max)
|
||||
/// base = 2 + 8 * (L_in / L_max) ^ (ln(bias) / ln(0.5))
|
||||
/// </summary>
|
||||
private Image<Gray, byte> DragoToneMapping(Image<Gray, byte> inputImage,
|
||||
double gamma, double bias)
|
||||
{
|
||||
int width = inputImage.Width;
|
||||
int height = inputImage.Height;
|
||||
|
||||
var floatImage = inputImage.Convert<Gray, float>();
|
||||
|
||||
// 归一化到 [0, 1]
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
floatImage.Data[y, x, 0] /= 255.0f;
|
||||
|
||||
// 全局最大亮�
|
||||
float maxLum = 0;
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
if (floatImage.Data[y, x, 0] > maxLum)
|
||||
maxLum = floatImage.Data[y, x, 0];
|
||||
|
||||
if (maxLum < 0.001f) maxLum = 0.001f;
|
||||
|
||||
double biasP = Math.Log(bias) / Math.Log(0.5);
|
||||
double divider = Math.Log10(1.0 + maxLum);
|
||||
if (divider < 0.001) divider = 0.001;
|
||||
|
||||
var result = new Image<Gray, float>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float lum = floatImage.Data[y, x, 0];
|
||||
// 自适应对数基底
|
||||
double adaptBase = 2.0 + 8.0 * Math.Pow(lum / maxLum, biasP);
|
||||
double logAdapt = Math.Log(1.0 + lum) / Math.Log(adaptBase);
|
||||
double mapped = logAdapt / divider;
|
||||
result.Data[y, x, 0] = (float)Math.Max(0, Math.Min(1.0, mapped));
|
||||
}
|
||||
}
|
||||
|
||||
// Gammaæ ¡æ£
|
||||
if (Math.Abs(gamma - 1.0) > 0.01)
|
||||
{
|
||||
double invGamma = 1.0 / gamma;
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
result.Data[y, x, 0] = (float)Math.Pow(result.Data[y, x, 0], invGamma);
|
||||
}
|
||||
|
||||
var byteResult = NormalizeToByteImage(result);
|
||||
|
||||
floatImage.Dispose();
|
||||
result.Dispose();
|
||||
|
||||
return byteResult;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// å�Œè¾¹æ»¤æ³¢è‰²è°ƒæ˜ å°„
|
||||
/// 使用�边滤波分离基础层和细节�
|
||||
/// å�Œè¾¹æ»¤æ³¢ä¿�è¾¹ç‰¹æ€§ä½¿å¾—ç»†èŠ‚å±‚æ›´åŠ ç²¾ç¡®
|
||||
/// </summary>
|
||||
private Image<Gray, byte> BilateralToneMapping(Image<Gray, byte> inputImage,
|
||||
double gamma, double sigmaSpace, double sigmaColor, double detailBoost)
|
||||
{
|
||||
int width = inputImage.Width;
|
||||
int height = inputImage.Height;
|
||||
|
||||
// 转�为浮点并�对�
|
||||
var floatImage = inputImage.Convert<Gray, float>();
|
||||
var logImage = new Image<Gray, float>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
logImage.Data[y, x, 0] = (float)Math.Log(floatImage.Data[y, x, 0] / 255.0f + 0.001);
|
||||
|
||||
// �边滤波��基础层(�边平滑�
|
||||
int diameter = (int)(sigmaSpace * 2) | 1;
|
||||
if (diameter < 3) diameter = 3;
|
||||
if (diameter > 31) diameter = 31;
|
||||
|
||||
var baseLayer = new Image<Gray, float>(width, height);
|
||||
// 转��byte 进行�边滤波,�转回 float
|
||||
var logNorm = NormalizeToByteImage(logImage);
|
||||
var baseNorm = new Image<Gray, byte>(width, height);
|
||||
CvInvoke.BilateralFilter(logNorm, baseNorm, diameter, sigmaColor, sigmaSpace);
|
||||
|
||||
// 将基础层转回浮点对数域
|
||||
double logMin = double.MaxValue, logMax = double.MinValue;
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float v = logImage.Data[y, x, 0];
|
||||
if (v < logMin) logMin = v;
|
||||
if (v > logMax) logMax = v;
|
||||
}
|
||||
|
||||
double logRange = logMax - logMin;
|
||||
if (logRange < 0.001) logRange = 0.001;
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
baseLayer.Data[y, x, 0] = (float)(baseNorm.Data[y, x, 0] / 255.0 * logRange + logMin);
|
||||
|
||||
// 细节�= 对数图� - 基础�
|
||||
var detailLayer = logImage - baseLayer;
|
||||
|
||||
// 压缩基础�
|
||||
double baseMin = double.MaxValue, baseMax = double.MinValue;
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float v = baseLayer.Data[y, x, 0];
|
||||
if (v < baseMin) baseMin = v;
|
||||
if (v > baseMax) baseMax = v;
|
||||
}
|
||||
|
||||
double bRange = baseMax - baseMin;
|
||||
if (bRange < 0.001) bRange = 0.001;
|
||||
double targetRange = Math.Log(256.0);
|
||||
double compression = targetRange / bRange;
|
||||
|
||||
// ��
|
||||
var combined = new Image<Gray, float>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
float compBase = (float)((baseLayer.Data[y, x, 0] - baseMin) * compression + Math.Log(0.01));
|
||||
combined.Data[y, x, 0] = compBase + detailLayer.Data[y, x, 0] * (float)detailBoost;
|
||||
}
|
||||
|
||||
// 指数��回线性域
|
||||
var linearResult = new Image<Gray, float>(width, height);
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
linearResult.Data[y, x, 0] = (float)Math.Exp(combined.Data[y, x, 0]);
|
||||
|
||||
// Gammaæ ¡æ£
|
||||
if (Math.Abs(gamma - 1.0) > 0.01)
|
||||
{
|
||||
double invGamma = 1.0 / gamma;
|
||||
double maxVal = 0;
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
if (linearResult.Data[y, x, 0] > maxVal) maxVal = linearResult.Data[y, x, 0];
|
||||
|
||||
if (maxVal > 0)
|
||||
for (int y = 0; y < height; y++)
|
||||
for (int x = 0; x < width; x++)
|
||||
linearResult.Data[y, x, 0] = (float)(Math.Pow(linearResult.Data[y, x, 0] / maxVal, invGamma) * maxVal);
|
||||
}
|
||||
|
||||
var result = NormalizeToByteImage(linearResult);
|
||||
|
||||
floatImage.Dispose();
|
||||
logImage.Dispose();
|
||||
logNorm.Dispose();
|
||||
baseNorm.Dispose();
|
||||
baseLayer.Dispose();
|
||||
detailLayer.Dispose();
|
||||
combined.Dispose();
|
||||
linearResult.Dispose();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 归一化浮点图åƒ�到å—节图åƒ�
|
||||
/// </summary>
|
||||
private Image<Gray, byte> NormalizeToByteImage(Image<Gray, float> floatImage)
|
||||
{
|
||||
double minVal = double.MaxValue;
|
||||
double maxVal = double.MinValue;
|
||||
|
||||
for (int y = 0; y < floatImage.Height; y++)
|
||||
for (int x = 0; x < floatImage.Width; x++)
|
||||
{
|
||||
float val = floatImage.Data[y, x, 0];
|
||||
if (val < minVal) minVal = val;
|
||||
if (val > maxVal) maxVal = val;
|
||||
}
|
||||
|
||||
var result = new Image<Gray, byte>(floatImage.Size);
|
||||
double range = maxVal - minVal;
|
||||
if (range > 0)
|
||||
{
|
||||
for (int y = 0; y < floatImage.Height; y++)
|
||||
for (int x = 0; x < floatImage.Width; x++)
|
||||
{
|
||||
int normalized = (int)((floatImage.Data[y, x, 0] - minVal) / range * 255.0);
|
||||
result.Data[y, x, 0] = (byte)Math.Max(0, Math.Min(255, normalized));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user