feat: 集成海康威视相机接口
- 新增 HikvisionCameraController 实现 ICameraController - CameraFactory 支持 Basler/Hikvision 动态切换(config.json 配置) - PixelConverter 支持 Bayer RG/GR/GB/BG 8-bit 解码 - 修复采集链断裂问题(finally 中触发下一帧) - 相机设置面板:宽高和像素格式改为只读显示 - NavigationPropertyPanelViewModel 日志和状态文本改为英文 - 添加 MvCameraControl.Net.dll 到 ExternalLibraries
This commit is contained in:
@@ -10,6 +10,7 @@ public static class PixelConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// 将原始像素数据转换为 WPF 的 BitmapSource 对象。
|
||||
/// 支持 Mono8、BGR8、RGB8、BGRA8 以及 Bayer 8-bit 格式(自动解码为 BGR24)。
|
||||
/// 返回的 BitmapSource 已调用 Freeze(),可跨线程访问。
|
||||
/// </summary>
|
||||
public static BitmapSource ToBitmapSource(byte[] pixelData, int width, int height, string pixelFormat)
|
||||
@@ -19,11 +20,23 @@ public static class PixelConverter
|
||||
if (height <= 0) throw new ArgumentException("Height must be a positive integer.", nameof(height));
|
||||
ArgumentNullException.ThrowIfNull(pixelFormat);
|
||||
|
||||
var (format, stride) = pixelFormat switch
|
||||
string normalized = NormalizePixelFormat(pixelFormat);
|
||||
|
||||
// Bayer 格式需要解码
|
||||
if (normalized.StartsWith("Bayer"))
|
||||
{
|
||||
byte[] bgrData = DemosaicBayer(pixelData, width, height, normalized);
|
||||
var bmp = BitmapSource.Create(width, height, 96, 96, PixelFormats.Bgr24, null, bgrData, width * 3);
|
||||
bmp.Freeze();
|
||||
return bmp;
|
||||
}
|
||||
|
||||
var (format, stride) = normalized switch
|
||||
{
|
||||
"Mono8" => (PixelFormats.Gray8, width),
|
||||
"BGR8" => (PixelFormats.Bgr24, width * 3),
|
||||
"BGRA8" => (PixelFormats.Bgra32, width * 4),
|
||||
"RGB8" => (PixelFormats.Rgb24, width * 3),
|
||||
_ => throw new NotSupportedException($"Pixel format '{pixelFormat}' is not supported.")
|
||||
};
|
||||
|
||||
@@ -31,4 +44,136 @@ public static class PixelConverter
|
||||
bitmap.Freeze();
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将不同 SDK 的像素格式名称统一为标准名称。
|
||||
/// </summary>
|
||||
private static string NormalizePixelFormat(string pixelFormat)
|
||||
{
|
||||
if (pixelFormat is "Mono8" or "BGR8" or "BGRA8" or "RGB8")
|
||||
return pixelFormat;
|
||||
|
||||
var upper = pixelFormat.ToUpperInvariant();
|
||||
|
||||
if (upper.Contains("MONO8")) return "Mono8";
|
||||
if (upper.Contains("BGR8")) return "BGR8";
|
||||
if (upper.Contains("BGRA8")) return "BGRA8";
|
||||
if (upper.Contains("RGB8") && !upper.Contains("BAYER")) return "RGB8";
|
||||
|
||||
// Bayer 格式
|
||||
if (upper.Contains("BAYERRG8") || upper.Contains("BAYER_RG8")) return "BayerRG8";
|
||||
if (upper.Contains("BAYERGR8") || upper.Contains("BAYER_GR8")) return "BayerGR8";
|
||||
if (upper.Contains("BAYERGB8") || upper.Contains("BAYER_GB8")) return "BayerGB8";
|
||||
if (upper.Contains("BAYERBG8") || upper.Contains("BAYER_BG8")) return "BayerBG8";
|
||||
|
||||
return pixelFormat;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 简单 Bayer 解码(双线性插值),输出 BGR24。
|
||||
/// </summary>
|
||||
private static byte[] DemosaicBayer(byte[] bayer, int width, int height, string pattern)
|
||||
{
|
||||
// pattern: BayerRG8, BayerGR8, BayerGB8, BayerBG8
|
||||
// RG: R G GR: G R GB: G B BG: B G
|
||||
// G B B G R G G R
|
||||
|
||||
int rRow, rCol; // 红色像素在2x2块中的位置
|
||||
switch (pattern)
|
||||
{
|
||||
case "BayerRG8": rRow = 0; rCol = 0; break;
|
||||
case "BayerGR8": rRow = 0; rCol = 1; break;
|
||||
case "BayerGB8": rRow = 1; rCol = 0; break;
|
||||
case "BayerBG8": rRow = 1; rCol = 1; break;
|
||||
default: rRow = 0; rCol = 0; break;
|
||||
}
|
||||
|
||||
byte[] bgr = new byte[width * height * 3];
|
||||
|
||||
for (int y = 0; y < height; y++)
|
||||
{
|
||||
for (int x = 0; x < width; x++)
|
||||
{
|
||||
int srcIdx = y * width + x;
|
||||
int dstIdx = (y * width + x) * 3;
|
||||
|
||||
// 确定当前像素在 Bayer 模式中的角色
|
||||
int py = (y + rRow) % 2; // 0=红行, 1=蓝行
|
||||
int px = (x + rCol) % 2; // 0=红列/蓝列, 1=绿列
|
||||
|
||||
byte r, g, b;
|
||||
|
||||
if (py == 0 && px == 0)
|
||||
{
|
||||
// 红色像素位置
|
||||
r = bayer[srcIdx];
|
||||
g = AvgNeighbors4(bayer, width, height, x, y);
|
||||
b = AvgDiagonal(bayer, width, height, x, y);
|
||||
}
|
||||
else if (py == 1 && px == 1)
|
||||
{
|
||||
// 蓝色像素位置
|
||||
b = bayer[srcIdx];
|
||||
g = AvgNeighbors4(bayer, width, height, x, y);
|
||||
r = AvgDiagonal(bayer, width, height, x, y);
|
||||
}
|
||||
else if (py == 0 && px == 1)
|
||||
{
|
||||
// 绿色像素(红行)
|
||||
g = bayer[srcIdx];
|
||||
r = AvgHorizontal(bayer, width, x, y);
|
||||
b = AvgVertical(bayer, width, height, x, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 绿色像素(蓝行)
|
||||
g = bayer[srcIdx];
|
||||
b = AvgHorizontal(bayer, width, x, y);
|
||||
r = AvgVertical(bayer, width, height, x, y);
|
||||
}
|
||||
|
||||
bgr[dstIdx] = b;
|
||||
bgr[dstIdx + 1] = g;
|
||||
bgr[dstIdx + 2] = r;
|
||||
}
|
||||
}
|
||||
|
||||
return bgr;
|
||||
}
|
||||
|
||||
private static byte AvgNeighbors4(byte[] data, int w, int h, int x, int y)
|
||||
{
|
||||
int sum = 0, count = 0;
|
||||
if (x > 0) { sum += data[y * w + x - 1]; count++; }
|
||||
if (x < w - 1) { sum += data[y * w + x + 1]; count++; }
|
||||
if (y > 0) { sum += data[(y - 1) * w + x]; count++; }
|
||||
if (y < h - 1) { sum += data[(y + 1) * w + x]; count++; }
|
||||
return count > 0 ? (byte)(sum / count) : (byte)0;
|
||||
}
|
||||
|
||||
private static byte AvgDiagonal(byte[] data, int w, int h, int x, int y)
|
||||
{
|
||||
int sum = 0, count = 0;
|
||||
if (x > 0 && y > 0) { sum += data[(y - 1) * w + x - 1]; count++; }
|
||||
if (x < w - 1 && y > 0) { sum += data[(y - 1) * w + x + 1]; count++; }
|
||||
if (x > 0 && y < h - 1) { sum += data[(y + 1) * w + x - 1]; count++; }
|
||||
if (x < w - 1 && y < h - 1) { sum += data[(y + 1) * w + x + 1]; count++; }
|
||||
return count > 0 ? (byte)(sum / count) : (byte)0;
|
||||
}
|
||||
|
||||
private static byte AvgHorizontal(byte[] data, int w, int x, int y)
|
||||
{
|
||||
int sum = 0, count = 0;
|
||||
if (x > 0) { sum += data[y * w + x - 1]; count++; }
|
||||
if (x < w - 1) { sum += data[y * w + x + 1]; count++; }
|
||||
return count > 0 ? (byte)(sum / count) : (byte)0;
|
||||
}
|
||||
|
||||
private static byte AvgVertical(byte[] data, int w, int h, int x, int y)
|
||||
{
|
||||
int sum = 0, count = 0;
|
||||
if (y > 0) { sum += data[(y - 1) * w + x]; count++; }
|
||||
if (y < h - 1) { sum += data[(y + 1) * w + x]; count++; }
|
||||
return count > 0 ? (byte)(sum / count) : (byte)0;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user