0

私はこれを理解しようとしています。プログラムで画像を比較しています。画像を比較しようとすると、ほとんどの場合、問題はありません。いくつかの異なる組み合わせを使用すると、このエラーが発生します。元々は画像フォーマットが違うからだと思っていました。そうではありませんが。

各画像を取得して他の画像と比較すると、正常に機能します。互いに比較した場合ではありません。エラーが表示される場所を以下にマークしました。

public Color this[int x, int y]
{
    get
    {
        int index = (x + (y * image.Width)) * 4;
        return Color.FromArgb(rgbValues[index + 3], rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]); //This line is where the index out of bounds of the array error happens.
    }

    set
    {
        int index = (x + (y * image.Width)) * 4;
        rgbValues[index] = value.B;
        rgbValues[index + 1] = value.G;
        rgbValues[index + 2] = value.R;
        rgbValues[index + 3] = value.A;
    }
}

/// <summary>
/// Width of the image. 
/// </summary>
public int Width
{
    get
    {
        return image.Width;
    }
}

/// <summary>
/// Height of the image. 
/// </summary>
public int Height
{
    get
    {
        return image.Height;
    }
}

/// <summary>
/// Returns the modified Bitmap. 
/// </summary>

助けていただければ幸いです。画像を組み合わせて使用​​した場合にのみ発生する理由がわかりません。

画像をどのようにロードするのか疑問に思っている場合は、vb.net で行います。C# コードは dll にあります。ビットマップをメモリにロードする vb.net コードを次に示します。また、画像サイズ: 431x253

Dim bm As Bitmap = Bitmap.FromFile(Label1.Text)
Dim bm2 As Bitmap = Bitmap.FromFile(Label2.Text)
' Dim pnt As Point = ImageFinder.Contains(bm, bm2)
Dim ir As New ImageChecker(bm, bm2)
Dim imageContains As Boolean = ir.findimageboolean()
MessageBox.Show(imageContains)

編集: DLL の完全なコードは次のとおりです。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.IO;


namespace ImageRecognition
{
    public class LockedFastImage
    {
        private Bitmap image;
        private byte[] rgbValues;
        private System.Drawing.Imaging.BitmapData bmpData;

        private IntPtr ptr;
        private int bytes;

        public LockedFastImage(Bitmap image)
        {
            this.image = image;
            Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
            bmpData = image.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, image.PixelFormat);

            ptr = bmpData.Scan0;
            bytes = Math.Abs(bmpData.Stride) * image.Height;
            rgbValues = new byte[bytes];
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
        }

        ~LockedFastImage()
        {
            // Try to unlock the bits. Who cares if it dont work...
            try
            {
                image.UnlockBits(bmpData);
            }
            catch { }
        }

        /// <summary>
        /// Returns or sets a pixel of the image. 
        /// </summary>
        /// <param name="x">x parameter of the pixel</param>
        /// <param name="y">y parameter of the pixel</param>
        public Color this[int x, int y]
        {
            get
            {
                int index = (x + (y * image.Width)) * 4;
                return Color.FromArgb(rgbValues[index + 3], rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);
            }

            set
            {
                int index = (x + (y * image.Width)) * 4;
                rgbValues[index] = value.B;
                rgbValues[index + 1] = value.G;
                rgbValues[index + 2] = value.R;
                rgbValues[index + 3] = value.A;
            }
        }

        /// <summary>
        /// Width of the image. 
        /// </summary>
        public int Width
        {
            get
            {
                return image.Width;
            }
        }

        /// <summary>
        /// Height of the image. 
        /// </summary>
        public int Height
        {
            get
            {
                return image.Height;
            }
        }

        /// <summary>
        /// Returns the modified Bitmap. 
        /// </summary>
        public Bitmap asBitmap()
        {
            System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
            return image;
        }
    }

    public class ImageChecker
    {

        private LockedFastImage big_image;
        private LockedFastImage small_image;
        /// <summary>
        /// The time needed for last operation.
        /// </summary>
        public TimeSpan time_needed = new TimeSpan();

        /// <summary>
        /// Error return value.
        /// </summary>
        static public Point CHECKFAILED = new Point(-1, -1);

        /// <summary>
        /// Constructor of the ImageChecker
        /// </summary>
        /// <param name="big_image">The image containing the small image.</param>
        /// <param name="small_image">The image located in the big image.</param>
        public ImageChecker(Bitmap big_image, Bitmap small_image)
        {
            this.big_image = new LockedFastImage(big_image);
            this.small_image = new LockedFastImage(small_image);
        }

        /// <summary>
        /// Returns the location of the small image in the big image. Returns CHECKFAILED if not found.
        /// </summary>
        /// <param name="x_speedUp">speeding up at x achsis.</param>
        /// <param name="y_speedUp">speeding up at y achsis.</param>
        /// <param name="begin_percent_x">Reduces the search rect. 0 - 100</param>
        /// <param name="end_percent_x">Reduces the search rect. 0 - 100</param>
        /// <param name="begin_percent_x">Reduces the search rect. 0 - 100</param>
        /// <param name="end_percent_y">Reduces the search rect. 0 - 100</param>
        public Point bigContainsSmall(int x_speedUp = 4, int y_speedUp = 4, int begin_percent_x = 0, int end_percent_x = 100, int begin_percent_y = 0, int end_percent_y = 100)
        {
            /*
             * SPEEDUP PARAMETER
             * It might be enough to check each second or third pixel in the small picture.
             * However... In most cases it would be enough to check 4 pixels of the small image for diablo porposes.
             * */

            /*
             * BEGIN, END PARAMETER
             * In most cases we know where the image is located, for this we have the begin and end paramenters.
             * */

            DateTime begin = DateTime.Now;

            if (x_speedUp < 1) x_speedUp = 1;
            if (y_speedUp < 1) y_speedUp = 1;
            if (begin_percent_x < 0 || begin_percent_x > 100) begin_percent_x = 0;
            if (begin_percent_y < 0 || begin_percent_y > 100) begin_percent_y = 0;
            if (end_percent_x < 0 || end_percent_x > 100) end_percent_x = 100;
            if (end_percent_y < 0 || end_percent_y > 100) end_percent_y = 100;

            int x_start = (int)((double)big_image.Width * ((double)begin_percent_x / 100.0));
            int x_end = (int)((double)big_image.Width * ((double)end_percent_x / 100.0));
            int y_start = (int)((double)big_image.Height * ((double)begin_percent_y / 100.0));
            int y_end = (int)((double)big_image.Height * ((double)end_percent_y / 100.0));

            /*
             * We cant speed up the big picture, because then we have to check pixels in the small picture equal to the speeded up size 
             * for each pixel in the big picture.
             * Would give no speed improvement.
             * */

            //+ 1 because first pixel is in picture. - small because image have to be fully in the other image
            for (int x = x_start; x < x_end - small_image.Width + 1; x++)
                for (int y = y_start; y < y_end - small_image.Height + 1; y++)
                {
                    //now we check if all pixels matches
                    for (int sx = 0; sx < small_image.Width; sx += x_speedUp)
                        for (int sy = 0; sy < small_image.Height; sy += y_speedUp)
                        {
                            if (small_image[sx, sy] != big_image[x + sx, y + sy])
                                goto CheckFailed;
                        }

                    //check ok
                    time_needed = DateTime.Now - begin;
                    return new Point(x, y);

                CheckFailed: ;
                }

            time_needed = DateTime.Now - begin;
            return CHECKFAILED;
        }
         public Boolean findimageboolean(int x_speedUp = 1, int y_speedUp = 1, int begin_percent_x = 0, int end_percent_x = 100, int begin_percent_y = 0, int end_percent_y = 100)
        {
            /*
             * SPEEDUP PARAMETER
             * It might be enough to check each second or third pixel in the small picture.
             * However... In most cases it would be enough to check 4 pixels of the small image for diablo porposes.
             * */

            /*
             * BEGIN, END PARAMETER
             * In most cases we know where the image is located, for this we have the begin and end paramenters.
             * */

            DateTime begin = DateTime.Now;

            if (x_speedUp < 1) x_speedUp = 1;
            if (y_speedUp < 1) y_speedUp = 1;
            if (begin_percent_x < 0 || begin_percent_x > 100) begin_percent_x = 0;
            if (begin_percent_y < 0 || begin_percent_y > 100) begin_percent_y = 0;
            if (end_percent_x < 0 || end_percent_x > 100) end_percent_x = 100;
            if (end_percent_y < 0 || end_percent_y > 100) end_percent_y = 100;

            int x_start = (int)((double)big_image.Width * ((double)begin_percent_x / 100.0));
            int x_end = (int)((double)big_image.Width * ((double)end_percent_x / 100.0));
            int y_start = (int)((double)big_image.Height * ((double)begin_percent_y / 100.0));
            int y_end = (int)((double)big_image.Height * ((double)end_percent_y / 100.0));

            /*
             * We cant speed up the big picture, because then we have to check pixels in the small picture equal to the speeded up size 
             * for each pixel in the big picture.
             * Would give no speed improvement.
             * */

            //+ 1 because first pixel is in picture. - small because image have to be fully in the other image
            for (int x = x_start; x < x_end - small_image.Width + 1; x++)
                for (int y = y_start; y < y_end - small_image.Height + 1; y++)
                {
                    //now we check if all pixels matches
                    for (int sx = 0; sx < small_image.Width; sx += x_speedUp)
                        for (int sy = 0; sy < small_image.Height; sy += y_speedUp)
                        {
                            if (small_image[sx, sy] != big_image[x + sx, y + sy])
                                goto CheckFailed;
                        }

                    //check ok
                    time_needed = DateTime.Now - begin;
                    return true;

                CheckFailed: ;
                }

            time_needed = DateTime.Now - begin;
             return false;
        }
    }
}
4

1 に答える 1

1

Strideが負の場合は、イメージ バイトを別の方法でコピーする必要があるため、コンストラクターにいくつかの変更を加えました。

    public LockedFastImage(Bitmap image)
    {
        this.image = image;
        Rectangle rect = new Rectangle(0, 0, image.Width, image.Height);
        bmpData = image.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

        ptr = bmpData.Scan0;
        bytes = Math.Abs(bmpData.Stride) * image.Height;
        rgbValues = new byte[bytes];
        if (bmpData.Stride < 0)
        {
            int lines, pos, BytesPerLine = Math.Abs(bmpData.Stride);
            for (lines = pos = 0; lines < image.Height; lines++, pos += BytesPerLine)
            {
                System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, pos, BytesPerLine);
                ptr = (IntPtr)(ptr.ToInt64() + bmpData.Stride);
            }
        }
        else
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
    }

ピクセル データを ARGB として保存する場合は、対応するPixelFormatを使用してピクセルをロックする必要があります。アクセサーも変更しました:

    public Color this[int x, int y]
    {
        get
        {
            int index = y * (image.Width << 2) + x;
            return Color.FromArgb(rgbValues[index + 3], rgbValues[index + 2], rgbValues[index + 1], rgbValues[index]);
        }

        set
        {
            int index = y * (image.Width << 2) + x;
            rgbValues[index] = value.B;
            rgbValues[index + 1] = value.G;
            rgbValues[index + 2] = value.R;
            rgbValues[index + 3] = value.A;
        }
    }

ところで、私はこのコードをテストしていません!


厳密に言えば、BytesPerLineプライベート フィールドとして保存してから、それを使用しindexてアクセサーで計算する必要があります。したがって、これを(image.Width << 2)に変更する必要がありますBytesPerLine
また、getアクセサーを次のように変更してみてください (高速かどうかはわかりませんが、必要に応じて試すことができます)。

    get
    {
        return Color.FromArgb(BitConverter.ToInt32(rgbValues, y * (image.Width << 2) + x));
    }
于 2013-01-08T07:03:20.297 に答える