如何在C#中仅读取16位图像的像素数据而非元数据?

3

我有一张16位深度的图像,我希望只读取像素数据并将其存储到byte[]中。

我尝试了以下代码:

FileInfo fileInfo = new FileInfo("C:\\Image stitch API\\Temp\\output.tif");
byte[] data = new byte[fileInfo.Length];

我也尝试了以下方法:
Image img;
img = Image.FromFile("C:\\Image stitch API\\Temp\\output.tif");
ImageConverter ic = new ImageConverter();
byte[] data = (byte[])ic.ConvertTo(img, typeof(byte[]))

这里所有的数据都来自于图像,但我只需要像素数据?

有人能帮忙吗?

2个回答

1
如果您可以将图像加载为位图,那么获取像素信息就非常容易,如下所示。
    Bitmap bitmap = new Bitmap("somefile.ext");
    Color color = bitmap.GetPixel(x,y) 

GetPixel()函数将返回Color(结构体)类型,您可以像下面所示一样获取单个通道值作为字节。

    byte g = slateBlue.G;
    byte b = slateBlue.B;
    byte r = slateBlue.R;
    byte a = slateBlue.A;

关于您的评论,我建议您使用Netvips来处理以字节数组形式表示的图像(这比system.drawing更好、更快)。通过这样做,您可以像下面这样获取图像带的字节数组。

    var imageBands = inputImage.Bandsplit();
    var R = imageBands [0];
    var B = imageBands [1];
    var G = imageBands [2];

如果您不想切换库,您可以使用System.Drawing获取字节数组,如下所示。
byte[] image = new[] {R, B, G};

这里我需要字节数组,我们如何指定byte[]的大小并向其中添加值。 - Dileep
结果是什么? - Hasan
我使用以下方法:
Bitmap bitmap = new Bitmap("somefile.ext");
byte[] data = nbyte[imgLength]; int pixelPosition = 0; for (int i = 0; i < img.Width; i++) { for (int j = 0; j < img.Height; j++) { Color pixel = img.GetPixel(i, j); data[pixelPosition] = pixel.R; data[pixelPosition + 1] = pixel.G; data[pixelPosition + 2] = pixel.B; data[pixelPosition + 3] = pixel.A; pixelPosition = pixelPosition + 4; } }
- Dileep
很好,希望解决了你的问题!如果是这样,请接受我的答案。 - Hasan

1
使用 LockBitsMarshal.Copy 是从图像中获取图像数据的最快方法。
强烈建议不要使用 GetPixel。你需要知道,LockBits 通过保留内存来放置图像的字节并将图像的字节复制到其中。然后,UnlockBits 将清除该保留的内存。
现在,问题是,GetPixel 对于每个访问的像素执行相同的锁定和解锁操作。使用 LockBits 一次相当快,但是为了数万个像素而一遍又一遍地使用它会使速度变慢。
请注意,有两种使用 LockBits 的方式;或者你使用 sourceImage.PixelFormat 获取原始像素格式的数据,或者你使用 LockBits 转换 数据以满足你的需求,通过提供不同的像素格式。在这段代码中,我强制输出像素格式为 Format32bppArgb
/// <summary>
/// Gets the raw bytes from an image.
/// </summary>
/// <param name="sourceImage">The image to get the bytes from.</param>
/// <returns>The raw bytes of the image</returns>
public static Byte[] GetImageDataAs32bppArgb(Bitmap sourceImage)
{
    BitmapData sourceData = sourceImage.LockBits(
        new Rectangle(0, 0, sourceImage.Width, sourceImage.Height),
        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    Byte[] data = new Byte[sourceData.Stride * sourceImage.Height];
    Marshal.Copy(sourceData.Scan0, data, 0, data.Length);
    sourceImage.UnlockBits(sourceData);
    return data;
}

Stride通常与width * bpp不同,因为位图的行始终对齐到4字节的倍数,因此通常需要返回它并跟踪它,但由于这是32位格式,其数据始终对齐到4字节,因此实际上没有差异。

请注意,您的问题从未指定您需要数据的顺序。典型的ARGB数据实际上按字节顺序B、G、R、A排列,因为IBM PC架构上的正常整数保存是小端序,这意味着Int32值“FFEEDDCC”的实际字节在内存中被反转为[CC DD EE FF]。但是,您在这里给出的另一个答案的评论似乎表明您选择了字节顺序[RR,GG,BB,AA]或“ABGR”,这是我从未见过的格式。但是,如果您确实需要更改它,只需添加一个简单的额外循环来交换红色和蓝色即可:

for (Int32 i = 0; i < data.Length; i += 4)
{
    Byte b = data[i]; // save Blue
    data[i] = data[i + 2]; // set first component to the Red value
    data[i + 2] = b; // set third component to the Blue value
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接