Files
XplorePlane/XP.ImageProcessing.Processors/Core/PixelDepthHelper.cs
T
李伟 6d27a02a9b feat(算子库): ImageProcessorBase 升级为泛型基类,支持 byte/ushort 双位深
- ImageProcessorBase<TDepth> 泛型基类,where TDepth : struct, IComparable
- 保留 ImageProcessorBase 别名(= ImageProcessorBase<byte>)完全向后兼容
- 新增 PixelDepthHelper 工具类,封装 byte/ushort 转换、像素读写、阈值缩放等通用操作
- MaxPixelValue 属性动态返回位深最大值(byte=255,ushort=65535)
2026-06-16 11:48:19 +08:00

181 lines
6.3 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================================
// Copyright © 2026 Hexagon Technology Center GmbH. All Rights Reserved.
// 文件名: PixelDepthHelper.cs
// 描述: 像素深度辅助工具,封装 8 位 / 16 位图像的常用类型转换操作
// 作者: 李伟 wei.lw.li@hexagon.com
// ============================================================================
using Emgu.CV;
using Emgu.CV.Structure;
namespace XP.ImageProcessing.Core;
/// <summary>
/// 像素深度辅助工具,封装 byte / ushort 之间的通用操作。
/// </summary>
public static class PixelDepthHelper
{
/// <summary>
/// 返回当前深度的最大像素值(byte → 255ushort → 65535)。
/// </summary>
public static int MaxValue<TDepth>() where TDepth : struct, IComparable
=> typeof(TDepth) == typeof(ushort) ? 65535 : 255;
/// <summary>
/// 将任意深度灰度图转换为 8 位灰度图(用于需要 CV_8U 输入的 OpenCV 函数)。
/// 16 位图按比例缩小到 [0, 255]。
/// </summary>
public static Image<Gray, byte> ToByteImage<TDepth>(Image<Gray, TDepth> src)
where TDepth : struct, IComparable
{
if (typeof(TDepth) == typeof(byte))
return (src as Image<Gray, byte>)!.Clone();
// 16-bit → 8-bit:右移 8 位(等价于除以 256)
var result = new Image<Gray, byte>(src.Width, src.Height);
var srcData = src.Data as ushort[,,];
if (srcData == null)
return src.Convert<Gray, byte>();
int h = src.Height, w = src.Width;
Parallel.For(0, h, y =>
{
for (int x = 0; x < w; x++)
result.Data[y, x, 0] = (byte)(srcData[y, x, 0] >> 8);
});
return result;
}
/// <summary>
/// 将 8 位图升位到目标深度(byte 直接 Cloneushort 左移 8 位)。
/// </summary>
public static Image<Gray, TDepth> FromByteImage<TDepth>(Image<Gray, byte> src)
where TDepth : struct, IComparable
{
if (typeof(TDepth) == typeof(byte))
return (src.Clone() as Image<Gray, TDepth>)!;
var result = new Image<Gray, ushort>(src.Width, src.Height);
int h = src.Height, w = src.Width;
Parallel.For(0, h, y =>
{
for (int x = 0; x < w; x++)
result.Data[y, x, 0] = (ushort)(src.Data[y, x, 0] << 8);
});
return (result as Image<Gray, TDepth>)!;
}
/// <summary>
/// 将 float 图像归一化到目标深度范围后转换。
/// </summary>
public static Image<Gray, TDepth> FromFloatImage<TDepth>(Image<Gray, float> src)
where TDepth : struct, IComparable
{
int maxVal = MaxValue<TDepth>();
int h = src.Height, w = src.Width;
// 求最小/最大值
double minV = double.MaxValue, maxV = double.MinValue;
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
float v = src.Data[y, x, 0];
if (v < minV) minV = v;
if (v > maxV) maxV = v;
}
}
double range = maxV - minV;
if (typeof(TDepth) == typeof(byte))
{
var result = new Image<Gray, byte>(w, h);
if (range > 1e-9)
{
Parallel.For(0, h, y =>
{
for (int x = 0; x < w; x++)
result.Data[y, x, 0] = (byte)Math.Clamp((int)((src.Data[y, x, 0] - minV) / range * 255.0), 0, 255);
});
}
return (result as Image<Gray, TDepth>)!;
}
else
{
var result = new Image<Gray, ushort>(w, h);
if (range > 1e-9)
{
Parallel.For(0, h, y =>
{
for (int x = 0; x < w; x++)
result.Data[y, x, 0] = (ushort)Math.Clamp((int)((src.Data[y, x, 0] - minV) / range * 65535.0), 0, 65535);
});
}
return (result as Image<Gray, TDepth>)!;
}
}
/// <summary>
/// 将目标深度图像转换为 float 图像(值域归一化到 0-1)。
/// </summary>
public static Image<Gray, float> ToFloatNormalized<TDepth>(Image<Gray, TDepth> src)
where TDepth : struct, IComparable
{
double maxVal = MaxValue<TDepth>();
int h = src.Height, w = src.Width;
var result = new Image<Gray, float>(w, h);
if (typeof(TDepth) == typeof(byte))
{
var srcByte = src.Data as byte[,,];
Parallel.For(0, h, y =>
{
for (int x = 0; x < w; x++)
result.Data[y, x, 0] = srcByte![y, x, 0] / 255f;
});
}
else
{
var srcUshort = src.Data as ushort[,,];
Parallel.For(0, h, y =>
{
for (int x = 0; x < w; x++)
result.Data[y, x, 0] = srcUshort![y, x, 0] / 65535f;
});
}
return result;
}
/// <summary>
/// 读取单个像素值(统一返回 int)。
/// </summary>
public static int ReadPixel<TDepth>(Image<Gray, TDepth> img, int y, int x)
where TDepth : struct, IComparable
{
if (typeof(TDepth) == typeof(byte))
return (img.Data as byte[,,])![y, x, 0];
return (img.Data as ushort[,,])![y, x, 0];
}
/// <summary>
/// 写入单个像素值(从 int 转换)。
/// </summary>
public static void WritePixel<TDepth>(Image<Gray, TDepth> img, int y, int x, int value)
where TDepth : struct, IComparable
{
if (typeof(TDepth) == typeof(byte))
(img.Data as byte[,,])![y, x, 0] = (byte)Math.Clamp(value, 0, 255);
else
(img.Data as ushort[,,])![y, x, 0] = (ushort)Math.Clamp(value, 0, 65535);
}
/// <summary>
/// 将参数中的阈值按目标深度归一化:
/// 8 位参数范围 0-25516 位参数范围 0-65535。
/// 如果旧参数最大值是 255,则对 16 位图自动按比例放大(×256)。
/// </summary>
public static int ScaleThreshold<TDepth>(int threshold8bit) where TDepth : struct, IComparable
=> typeof(TDepth) == typeof(ushort) ? threshold8bit * 256 : threshold8bit;
}