3

主題が言うように、私は.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 イメージで動作させることができますか? ポインターから 1 バイトだけを読み取った場合、常に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;
    }
4

2 に答える 2

2

はい、わかった!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 
于 2012-08-25T15:17:40.427 に答える
1

まず、1 回の操作で 1 つのピクセルを読み取る必要がある場合GetPixel、パフォーマンスは同等になります。高価な操作は、ビットをロックすることです。必要なすべての読み取りを行うために保持しBitmapData、最後にのみ閉じる必要があります-ただし、閉じることを忘れないでください!

ピクセル形式について混乱があるようですが、正しい 1bpp であると仮定しましょう。この場合、各ピクセルは 1 ビットを占有し、1 バイトには 8 ピクセルのデータが存在します。したがって、インデックスの計算は正しくありません。バイトの位置は になります。x/8次に、ビットを取る必要がありますx%8

于 2012-08-25T14:16:44.290 に答える