6

ロックビットを使用して画像検出クラスを増やしようとしていますが、コードに問題が発生するため、実行されません。画像検出を高速化するためにロックビットとgetpixelを同時に使用するにはどうすればよいですか、または誰かが同じくらい高速な代替手段を見せてくれますか?

コード:

static IntPtr Iptr = IntPtr.Zero;
    static BitmapData bitmapData = null;
    static public byte[] Pixels { get; set; }
    static public int Depth { get; private set; }
    static public int Width { get; private set; }
    static public int Height { get; private set; }

    static public void LockBits(Bitmap source)

    {
            // Get width and height of bitmap
            Width = source.Width;
            Height = source.Height;

            // get total locked pixels count
            int PixelCount = Width * Height;

            // Create rectangle to lock
            Rectangle rect = new Rectangle(0, 0, Width, Height);

            // get source bitmap pixel format size
            Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);


            // Lock bitmap and return bitmap data
            bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
                                         source.PixelFormat);

            // create byte array to copy pixel values
            int step = Depth / 8;
            Pixels = new byte[PixelCount * step];
            Iptr = bitmapData.Scan0;

            // Copy data from pointer to array
            Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);

    }


     static public bool SimilarColors(int R1, int G1, int B1, int R2, int G2, int B2, int Tolerance)
    {
        bool returnValue = true;
        if (Math.Abs(R1 - R2) > Tolerance || Math.Abs(G1 - G2) > Tolerance || Math.Abs(B1 - B2) > Tolerance)
        {
            returnValue = false;
        }
        return returnValue;
    }


     public bool findImage(Bitmap small, Bitmap large, out Point location)
     {
         unsafe
         {
             LockBits(small);
             LockBits(large);
             //Loop through large images width
             for (int largeX = 0; largeX < large.Width; largeX++)
             {
                 //And height
                 for (int largeY = 0; largeY < large.Height; largeY++)
                 {
                     //Loop through the small width
                     for (int smallX = 0; smallX < small.Width; smallX++)
                     {
                         //And height
                         for (int smallY = 0; smallY < small.Height; smallY++)
                         {
                             //Get current pixels for both image
                             Color currentSmall = small.GetPixel(smallX, smallY);
                             Color currentLarge = large.GetPixel(largeX + smallX, largeY + smallY);
                             //If they dont match (i.e. the image is not there)

                             if (!colorsMatch(currentSmall, currentLarge))
                                 //Goto the next pixel in the large image

                                 goto nextLoop;
                         }
                     }
                     //If all the pixels match up, then return true and change Point location to the top left co-ordinates where it was found
                     location = new Point(largeX, largeY);
                     return true;
                 //Go to next pixel on large image
                 nextLoop:
                     continue;
                 }
             }
             //Return false if image is not found, and set an empty point
             location = Point.Empty;
             return false;
         }
     }
4

2 に答える 2

7

画像処理にgetPixel()を使用することは望ましくありません。ポイント値を取得するために時々呼び出しを行うことは問題ありませんが(たとえば、マウスオーバー時)、一般に、必要に応じてビットマップに変換できる画像メモリまたは2D配列で画像処理を行うことをお勧めします。

まず、LockBits / UnlockBitsを使用して、操作に便利な配列を抽出するメソッドを作成してみてください。配列の操作が完了したら、別のLockBits/UnlockBits関数を使用して配列をビットマップに書き戻すことができます。

これが私が過去に使用したいくつかのサンプルコードです。最初の関数は、ビットマップから値の1D配列を返します。ビットマップの幅がわかっているので、この1D配列を2D配列に変換してさらに処理することができます。処理が完了したら、2番目の関数を呼び出して、(変更された)1D配列を再びビットマップに変換できます。

public static byte[] Array1DFromBitmap(Bitmap bmp){
    if (bmp == null) throw new NullReferenceException("Bitmap is null");

    Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
    BitmapData data = bmp.LockBits(rect, ImageLockMode.ReadWrite, bmp.PixelFormat);
    IntPtr ptr = data.Scan0;

    //declare an array to hold the bytes of the bitmap
    int numBytes = data.Stride * bmp.Height;
    byte[] bytes = new byte[numBytes];

    //copy the RGB values into the array
    System.Runtime.InteropServices.Marshal.Copy(ptr, bytes, 0, numBytes);

    bmp.UnlockBits(data);

    return bytes;           
}

public static Bitmap BitmapFromArray1D(byte[] bytes, int width, int height)
{
    Bitmap grayBmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
    Rectangle grayRect = new Rectangle(0, 0, grayBmp.Width, grayBmp.Height);
    BitmapData grayData = grayBmp.LockBits(grayRect, ImageLockMode.ReadWrite, grayBmp.PixelFormat);
    IntPtr grayPtr = grayData.Scan0;

    int grayBytes = grayData.Stride * grayBmp.Height;
    ColorPalette pal = grayBmp.Palette;

    for (int g = 0; g < 256; g++){
        pal.Entries[g] = Color.FromArgb(g, g, g);
    }

    grayBmp.Palette = pal;

    System.Runtime.InteropServices.Marshal.Copy(bytes, 0, grayPtr, grayBytes);

    grayBmp.UnlockBits(grayData);
    return grayBmp;
}

これらのメソッドは、ビットマップピクセル形式について想定しているため、機能しない可能性がありますが、一般的な考え方が明確であることを願っています。LockBits/ UnlockBitsを使用して、ビットマップからバイトの配列を抽出し、アルゴリズムの記述とデバッグを最も簡単に行えるようにします。次に、LockBits / UnlockBitsを再度使用して、配列をビットマップに再度書き込みます。

移植性のために、メソッド自体の中でグローバル変数を操作するのではなく、メソッドが目的のデータ型を返すことをお勧めします。

getPixel()を使用している場合は、上記のように配列との間で変換を行うと、わずかなコーディング作業でコードを大幅に高速化できます。

于 2012-09-21T00:26:23.600 に答える
2

どこから始めればいいですか。lockBitsで何をしているのかをよく理解してください。まず、バイト配列をで上書きしないように注意してください。

LockBits(small);              
LockBits(large);

2回目の呼び出しのため、最初の呼び出しは画像をロックするだけですが、再度ロックを解除しないため、これは適切ではありません。したがって、画像を表す別のバイト配列を追加します。あなたはこのようなことをすることができます

LockBits(small, true);              
LockBits(large, false);

Lockbitsメソッドを変更します

static public void LockBits(Bitmap source, bool flag)                        
{   
...
Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);

if(flag)
   PixelsSmall=Pixels;
else
   PixelsLarge=Pixels;
}

ここで、PixelsLargeとPixelsSmallはグローバルであり、Pixelsはグローバルではありません。これら2つには画像が含まれています。今、あなたはそれを比較する必要があります。次に、各「バイトのセット」を比較する必要があるため、Pixelformatを知る必要があります。32b / pix 24ですか、それとも8(ARGB、RGB、グレースケール)のみです。ARGB画像を撮りましょう。この場合、セットは4バイト(= 32/8)で構成されます。順序はわかりませんが、1セットの順序はABGRまたはBGRAだと思います。

これがお役に立てば幸いです。適切なピクセルを比較する方法がわからない場合は、もう一度質問してください。ああ、UnlockBitsコマンドを使用することを忘れないでください。

于 2012-08-29T06:59:34.127 に答える