使用C#获取单色图像的像素颜色

问题描述 投票:0回答:2

正如主题所说,我有一个

.bmp
图像,我需要编写一个代码来获取图像任何像素的颜色。它是 1bpp(索引)图像,因此颜色将为黑色或白色。这是我目前拥有的代码:

    //This method locks the bits of line of pixels
    private BitmapData LockLine(Bitmap bmp, int y)
    {
        Rectangle lineRect = new Rectangle(0, y, bmp.Width, 1);
        BitmapData line = bmp.LockBits(lineRect,
                                       ImageLockMode.ReadWrite,
                                       bmp.PixelFormat);
        return line;
    }
    //This method takes the BitmapData of a line of pixels
    //and returns the color of one which has the needed x coordinate
    private Color GetPixelColor(BitmapData data, int x)
    {
        //I am not sure if this line is correct
        IntPtr pPixel = data.Scan0 + x; 
        //The following code works for the 24bpp image:
        byte[] rgbValues = new byte[3];
        System.Runtime.InteropServices.Marshal.Copy(pPixel, rgbValues, 0, 3);
        return Color.FromArgb(rgbValues[2], rgbValues[1], rgbValues[0]);
    }

但是我怎样才能使它适用于 1bpp 图像呢?如果我只从指针读取一个字节,它总是具有

255
值,所以我认为,我做错了什么。
请不要建议使用
System.Drawing.Bitmap.GetPixel
方法,因为它运行速度太慢,而我希望代码运行得尽可能快。 预先感谢。

编辑: 这是运行良好的代码,以防万一有人需要这个:

    private Color GetPixelColor(BitmapData data, int x)
    {
        int byteIndex = x / 8;
        int bitIndex = x % 8;
        IntPtr pFirstPixel = data.Scan0+byteIndex;
        byte[] color = new byte[1];
        System.Runtime.InteropServices.Marshal.Copy(pFirstPixel, color, 0, 1);
        BitArray bits = new BitArray(color);
        return bits.Get(bitIndex) ? Color.Black : Color.White;
    }
c# image image-processing monochrome
2个回答
3
投票

好的,明白了!您需要从 BitmapData 中读取位,并对要提取颜色的位应用掩码:

var bm = new Bitmap...

//lock all image bits
var bitmapData = bm.LockBits(new Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed);

// this  will return the pixel index in the color pallete
// since is 1bpp it will return 0 or 1
int pixelColorIndex = GetIndexedPixel(50, 30, bitmapData);

// read the color from pallete
Color pixelColor = bm.Pallete.Entries[pixelColorIndex];

方法如下:

// x, y relative to the locked area
private int GetIndexedPixel(int x, int y, BitmapData bitmapData)
{
    var index = y * bitmapData.Stride + (x >> 3);
    var chunk = Marshal.ReadByte(bitmapData.Scan0, index);

    var mask = (byte)(0x80 >> (x & 0x7));
    return (chunk & mask) == mask ? 1 : 0;
}

像素位置分2轮计算:

  1. 查找 'x' 中像素为 (x / 8) 的字节:每个字节包含 8 个像素,找到字节除法 x/8 向下舍入:58 >> 3 = 7,该像素在第 7 个字节上当前行(步幅)

  2. 查找当前字节上的位 (x % 8):执行

    x & 0x7
    仅获取最左边的 3 位 (x % 8)

示例:

x = 58 
// x / 8 - the pixel is on byte 7
byte = 58 >> 3 = 58 / 8 = 7 

// x % 8 - byte 7, bit 2
bitPosition = 58 & 0x7 = 2 

// the pixels are read from left to right, so we start with 0x80 and then shift right. 
mask = 0x80 >> bitPosition = 1000 0000b >> 2 =  0010 0000b 

1
投票

首先,如果您需要在一次操作中读取单个像素,那么

GetPixel
在性能上是相当的。昂贵的操作是锁定位,即。您应该按住
BitmapData
来完成您需要的所有阅读,并且仅在最后关闭它 - 但请记住关闭它!

您的像素格式似乎有些混乱,但我们假设它是正确的 1bpp。那么每个像素就占一位,一个字节就有8个像素的数据。因此,您的索引计算不正确。字节的位置将在

x/8
中,那么您需要取位
x%8

© www.soinside.com 2019 - 2024. All rights reserved.