using System.Windows.Media; using System.Windows.Media.Imaging; namespace XP.Camera; /// /// 提供像素数据到 WPF BitmapSource 的转换工具方法。 /// public static class PixelConverter { /// /// 将原始像素数据转换为 WPF 的 BitmapSource 对象。 /// 支持 Mono8、BGR8、RGB8、BGRA8 以及 Bayer 8-bit 格式(自动解码为 BGR24)。 /// 返回的 BitmapSource 已调用 Freeze(),可跨线程访问。 /// public static BitmapSource ToBitmapSource(byte[] pixelData, int width, int height, string pixelFormat) { ArgumentNullException.ThrowIfNull(pixelData); if (width <= 0) throw new ArgumentException("Width must be a positive integer.", nameof(width)); if (height <= 0) throw new ArgumentException("Height must be a positive integer.", nameof(height)); ArgumentNullException.ThrowIfNull(pixelFormat); 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.") }; var bitmap = BitmapSource.Create(width, height, 96, 96, format, null, pixelData, stride); bitmap.Freeze(); return bitmap; } /// /// 将不同 SDK 的像素格式名称统一为标准名称。 /// 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; } /// /// 简单 Bayer 解码(双线性插值),输出 BGR24。 /// 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; } }