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 回答