Basler USB 相机字节缓冲区到图像的转换
Basler USB Camera byte buffer to image conversion
我有 Basler acA3800 USB 相机。
IGrabResult grabResult = camera.StreamGrabber.RetrieveResult(5000, TimeoutHandling.ThrowException);
// Image grabbed successfully?
if (grabResult.GrabSucceeded)
{
byte[] buffer = grabResult.PixelData as byte[];
ImageWindow.DisplayImage(0, grabResult);
pictureBox1.Image = ImageFromRawBgraArray(buffer,3840,2748,PixelFormat.Format8bppIndexed);
MessageBox.Show(grabResult.PixelTypeValue.ToString());
}
此代码部分显示图像自身 window。
我有拍摄图像的像素数据。原始图像很好,但是当我将其转换为图像时,它损坏了。这是我的转换函数。
public Image ImageFromRawBgraArray(byte[] arr, int width, int height, PixelFormat pixelFormat)
{
var output = new Bitmap(width, height, pixelFormat);
var rect = new Rectangle(0, 0, width, height);
var bmpData = output.LockBits(rect, ImageLockMode.ReadWrite, output.PixelFormat);
// Row-by-row copy
var arrRowLength = width * Image.GetPixelFormatSize(output.PixelFormat) / 8;
var ptr = bmpData.Scan0;
for (var i = 0; i < height; i++)
{
Marshal.Copy(arr, i * arrRowLength, ptr, arrRowLength);
ptr += bmpData.Stride;
}
output.UnlockBits(bmpData);
return output;
}
我认为是关于像素类型的。我选择了 PixelFormat.Format8bppIndexed 的像素格式。其他的不行。
MessageBox.Show(grabResult.PixelTypeValue.ToString());
这个消息框给了我像素类型。它说 "BayerBG8"。这是什么意思?我应该怎么做才能获得清晰的图像?
你得到的奇怪颜色是因为 Format8bppIndexed
是调色板,你从不编辑调色板,这意味着它保留了默认生成的 Windows 调色板。但在你的情况下,这个调色板是无关紧要的,因为图像是 而不是 8 位索引格式;需要对其进行处理以将其转换为 RGB。
BayerBG8 的快速 google 让我 this page。那里的 Bayer 部分表明,在图像上使用特定图案的索引作为 R、G 和 B 是一种相当特殊的转换。
维基百科有 a whole article on how this stuff is generally processed, but this YouTube video 显示基础知识:
注意这是滑动window;对于第一个像素,颜色为
R G
G B
但对于第二个像素,它们将是
G R
B G
向下一行,第一个将使用
G B
R G
您最终会得到一个比给定尺寸宽和高少一个像素的图像,因为每行的最后一个像素和最后一行的所有像素都没有获取所需的相邻数据他们的完整像素数据。显然有更高级的算法可以解决这个问题,但对于这种方法,我将只介绍基本的滑动 window 方法。
public static Byte[] BayerToRgb(Byte[] arr, ref Int32 width, ref Int32 height, ref Int32 stride, Boolean greenFirst, Boolean blueRowFirst)
{
Int32 actualWidth = width - 1;
Int32 actualHeight = height - 1;
Int32 actualStride = actualWidth*3;
Byte[] result = new Byte[actualStride*actualHeight];
for (Int32 y = 0; y < actualHeight; y++)
{
Int32 curPtr = y*stride;
Int32 resPtr = y*actualStride;
Boolean blueRow = y % 2 == (blueRowFirst ? 0 : 1);
for (Int32 x = 0; x < actualWidth; x++)
{
// Get correct colour components from sliding window
Boolean isGreen = (x + y) % 2 == (greenFirst ? 0 : 1);
Byte cornerCol1 = isGreen ? arr[curPtr + 1] : arr[curPtr];
Byte cornerCol2 = isGreen ? arr[curPtr + stride] : arr[curPtr + stride + 1];
Byte greenCol1 = isGreen ? arr[curPtr] : arr[curPtr + 1];
Byte greenCol2 = isGreen ? arr[curPtr + stride + 1] : arr[curPtr + stride];
Byte blueCol = blueRow ? cornerCol1 : cornerCol2;
Byte redCol = blueRow ? cornerCol2 : cornerCol1;
// 24bpp RGB is saved as [B, G, R].
// Blue
result[resPtr + 0] = blueCol;
// Green
result[resPtr + 1] = (Byte) ((greenCol1 + greenCol2)/2);
// Red
result[resPtr + 2] = redCol;
curPtr++;
resPtr+=3;
}
}
height = actualHeight;
width = actualWidth;
stride = actualStride;
return result;
}
参数greenFirst
和blueRowFirst
表示绿色是否是图像上第一个遇到的像素,蓝色像素是在第一行还是第二行。对于你的“BG”格式,这两个都应该是错误的。
根据此结果,调整宽度、高度和步幅后,您可以使用您已经使用的方法将其转换为新图像,但像素格式为 Format24bppRgb
。
就我个人而言,我使用了一种更高级的方法,该方法考虑了输入步幅并且可以处理索引内容。如果你有兴趣,那个方法 can be found here.
注意上面的去马赛克方法非常基本,会显示many of the expected artifacts。有更先进的方法来分析数据并根据图像的拍摄方式获得更准确的结果,但您可能需要花费大量研究才能弄清楚所有这些并自行实施。
这是我做的一个小测试,从 a Bayer-filtered image I found online(第一张图片)开始,我将其转换为 8 位数组(此处显示为灰度;第二张图片)。如您所见,我自己的去马赛克(第三张图片)远不如他们从中得到的校正版本(第四张图片)准确,而且,值得注意的是,它小了一个像素,因此显示了一个白色边框。
(请注意,与上面的示例不同,此图像以绿色像素开头,这意味着必须调整解码参数)
我有 Basler acA3800 USB 相机。
IGrabResult grabResult = camera.StreamGrabber.RetrieveResult(5000, TimeoutHandling.ThrowException);
// Image grabbed successfully?
if (grabResult.GrabSucceeded)
{
byte[] buffer = grabResult.PixelData as byte[];
ImageWindow.DisplayImage(0, grabResult);
pictureBox1.Image = ImageFromRawBgraArray(buffer,3840,2748,PixelFormat.Format8bppIndexed);
MessageBox.Show(grabResult.PixelTypeValue.ToString());
}
此代码部分显示图像自身 window。
我有拍摄图像的像素数据。原始图像很好,但是当我将其转换为图像时,它损坏了。这是我的转换函数。
public Image ImageFromRawBgraArray(byte[] arr, int width, int height, PixelFormat pixelFormat)
{
var output = new Bitmap(width, height, pixelFormat);
var rect = new Rectangle(0, 0, width, height);
var bmpData = output.LockBits(rect, ImageLockMode.ReadWrite, output.PixelFormat);
// Row-by-row copy
var arrRowLength = width * Image.GetPixelFormatSize(output.PixelFormat) / 8;
var ptr = bmpData.Scan0;
for (var i = 0; i < height; i++)
{
Marshal.Copy(arr, i * arrRowLength, ptr, arrRowLength);
ptr += bmpData.Stride;
}
output.UnlockBits(bmpData);
return output;
}
我认为是关于像素类型的。我选择了 PixelFormat.Format8bppIndexed 的像素格式。其他的不行。
MessageBox.Show(grabResult.PixelTypeValue.ToString());
这个消息框给了我像素类型。它说 "BayerBG8"。这是什么意思?我应该怎么做才能获得清晰的图像?
你得到的奇怪颜色是因为 Format8bppIndexed
是调色板,你从不编辑调色板,这意味着它保留了默认生成的 Windows 调色板。但在你的情况下,这个调色板是无关紧要的,因为图像是 而不是 8 位索引格式;需要对其进行处理以将其转换为 RGB。
BayerBG8 的快速 google 让我 this page。那里的 Bayer 部分表明,在图像上使用特定图案的索引作为 R、G 和 B 是一种相当特殊的转换。
维基百科有 a whole article on how this stuff is generally processed, but this YouTube video 显示基础知识:
注意这是滑动window;对于第一个像素,颜色为
R G G B
但对于第二个像素,它们将是
G R B G
向下一行,第一个将使用
G B R G
您最终会得到一个比给定尺寸宽和高少一个像素的图像,因为每行的最后一个像素和最后一行的所有像素都没有获取所需的相邻数据他们的完整像素数据。显然有更高级的算法可以解决这个问题,但对于这种方法,我将只介绍基本的滑动 window 方法。
public static Byte[] BayerToRgb(Byte[] arr, ref Int32 width, ref Int32 height, ref Int32 stride, Boolean greenFirst, Boolean blueRowFirst)
{
Int32 actualWidth = width - 1;
Int32 actualHeight = height - 1;
Int32 actualStride = actualWidth*3;
Byte[] result = new Byte[actualStride*actualHeight];
for (Int32 y = 0; y < actualHeight; y++)
{
Int32 curPtr = y*stride;
Int32 resPtr = y*actualStride;
Boolean blueRow = y % 2 == (blueRowFirst ? 0 : 1);
for (Int32 x = 0; x < actualWidth; x++)
{
// Get correct colour components from sliding window
Boolean isGreen = (x + y) % 2 == (greenFirst ? 0 : 1);
Byte cornerCol1 = isGreen ? arr[curPtr + 1] : arr[curPtr];
Byte cornerCol2 = isGreen ? arr[curPtr + stride] : arr[curPtr + stride + 1];
Byte greenCol1 = isGreen ? arr[curPtr] : arr[curPtr + 1];
Byte greenCol2 = isGreen ? arr[curPtr + stride + 1] : arr[curPtr + stride];
Byte blueCol = blueRow ? cornerCol1 : cornerCol2;
Byte redCol = blueRow ? cornerCol2 : cornerCol1;
// 24bpp RGB is saved as [B, G, R].
// Blue
result[resPtr + 0] = blueCol;
// Green
result[resPtr + 1] = (Byte) ((greenCol1 + greenCol2)/2);
// Red
result[resPtr + 2] = redCol;
curPtr++;
resPtr+=3;
}
}
height = actualHeight;
width = actualWidth;
stride = actualStride;
return result;
}
参数greenFirst
和blueRowFirst
表示绿色是否是图像上第一个遇到的像素,蓝色像素是在第一行还是第二行。对于你的“BG”格式,这两个都应该是错误的。
根据此结果,调整宽度、高度和步幅后,您可以使用您已经使用的方法将其转换为新图像,但像素格式为 Format24bppRgb
。
就我个人而言,我使用了一种更高级的方法,该方法考虑了输入步幅并且可以处理索引内容。如果你有兴趣,那个方法 can be found here.
注意上面的去马赛克方法非常基本,会显示many of the expected artifacts。有更先进的方法来分析数据并根据图像的拍摄方式获得更准确的结果,但您可能需要花费大量研究才能弄清楚所有这些并自行实施。
这是我做的一个小测试,从 a Bayer-filtered image I found online(第一张图片)开始,我将其转换为 8 位数组(此处显示为灰度;第二张图片)。如您所见,我自己的去马赛克(第三张图片)远不如他们从中得到的校正版本(第四张图片)准确,而且,值得注意的是,它小了一个像素,因此显示了一个白色边框。
(请注意,与上面的示例不同,此图像以绿色像素开头,这意味着必须调整解码参数)