Trying to match pixel colour then click [C#]

左心房为你撑大大i 提交于 2020-07-09 19:15:10

问题


I need help getting my program to match the "stored" colour with the current one in the same location then click the mouse if it's the same. The grabbing of the colour works great so far in my code just unsure how to match a colour and a point, etc.

Also a start/stop button for the loop would be nice.

My code so far:

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Pixel_detection_test_3
{
    public partial class PixelDetectionForm : Form
    {
        private const UInt32 MOUSEEVENTF_LEFTDOWN = 0x0002;
        private const UInt32 MOUSEEVENTF_LEFTUP = 0x0004;

        [DllImport("user32.dll")]
        private static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint dwData, uint dwExtraInf);

        private int pixelY;
        private int pixelX;
        private Point pixelYX;
        private static Color currentColour;
        private static Color storedColour;

        public PixelDetectionForm()
        {
            InitializeComponent();
        }

        static Color GetPixel(Point position)
        {
            using (var bitmap = new Bitmap(1, 1))
            {
                using (var graphics = Graphics.FromImage(bitmap))
                {
                    graphics.CopyFromScreen(position, new Point(0, 0), new Size(1, 1));
                }
                return bitmap.GetPixel(0, 0);
            }
        }

        private void PixelDetectionForm_KeyDown(object sender, KeyEventArgs e)
        {
            // Get Cursor Pixel Position
            if (e.KeyCode == Keys.F1 || e.KeyCode == Keys.F2)
            {
                pixelY = Cursor.Position.Y;
                pixelX = Cursor.Position.X;
                pixelYX = Cursor.Position;
                textBoxYPos.Text = pixelY.ToString();
                textBoxXPos.Text = pixelX.ToString();
                e.Handled = true;
            }
            // Get Cursor Pixel Colour
            if (e.KeyCode == Keys.F1 || e.KeyCode == Keys.F3)
            {
                storedColour = GetPixel(Cursor.Position);
                textBoxColour.Text = storedColour.ToString().Remove(0, 14).TrimEnd(']');
                panelColourDisplay.BackColor = storedColour;
                e.Handled = true;
            }
        }

        // Not working, need help with this
        private async void buttonStart_Click(object sender, EventArgs e)
        {
            while (true)
            {
                GetPixel(pixelYX);

                // Should get position of 'pixelY' and 'pixelX'
                panelColourDisplay2.BackColor = GetPixel(Cursor.Position);

                if (pixelYX == storedColour)
                {
                    MousePress();
                }
                // Need this to prevent not responding
                await Task.Delay(3);
            }
        }

        private void MousePress()
        {
            mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
            mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
        }

        private void PixelDetectionForm_Click(object sender, EventArgs e)
        {
            ActiveControl = null;
        }

        private void PixelDetectionForm_Activated(object sender, EventArgs e)
        {
            ActiveControl = null;
        }
    }
}

Thanks


回答1:


Well, an alternative to the while..loop is using a Timer to achieve that.

using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace Pixel_detection_test_3
{
    public partial class PixelDetectionForm : Form
    {
        private readonly Timer Tmr;

        private Point lastPoint;
        //Assign this from your input code.
        private Color targetColor;

        public PixelDetectionForm()
        {
            Tmr = new Timer { Interval = 50 };
            Tmr.Tick += (s, e) => FindMatches(Cursor.Position);
        }
        //...

In the timer's Tick event, the FindMatches(..) method is called to check the current Cursor.Position and add the distinct matches into a ListBox. You can replace the last part with what you really need to do when you find a match. Like calling the MousePress() method in your code:

        //...
        private void FindMatches(Point p)
        {
            //To avoid the redundant calls..
            if (p.Equals(lastPoint)) return;

            lastPoint = p;

            using (var b = new Bitmap(1, 1))
            using (var g = Graphics.FromImage(b))
            {
                g.CopyFromScreen(p, Point.Empty, b.Size);

                var c = b.GetPixel(0, 0);

                if (c.ToArgb().Equals(targetColor.ToArgb()) &&
                    !listBox1.Items.Cast<Point>().Contains(p))
                {
                    listBox1.Items.Add(p);
                    listBox1.SelectedItem = p;
                }
            }
        }

        private void PixelDetectionForm_FormClosing(object sender, FormClosingEventArgs e)
        {
            Tmr.Dispose();
        }
    }
}

Start and stop the timer in the click events of the Start and Stop buttons.

Here's a demo:

Another alternative is to use the Global Mouse and Keyboard Hooks. Check this, this, and this for more details.


Edit 2/11/2020

If you just want to check whether a given color at a given point exists in a given image, then you can do:

private void buttonStart_Click(object sender, EventArgs e)
{
    var targetColor = ...; //target color.
    var targetPoint = ...; //target point.
    var sz = Screen.PrimaryScreen.Bounds.Size;
    using (var b = new Bitmap(sz.Width, sz.Height, PixelFormat.Format32bppArgb))
    using (var g = Graphics.FromImage(b))
    {
        g.CopyFromScreen(Point.Empty, Point.Empty, b.Size, CopyPixelOperation.SourceCopy);

        var bmpData = b.LockBits(new Rectangle(Point.Empty, sz), ImageLockMode.ReadOnly, b.PixelFormat);
        var pixBuff = new byte[bmpData.Stride * bmpData.Height];

        Marshal.Copy(bmpData.Scan0, pixBuff, 0, pixBuff.Length);

        b.UnlockBits(bmpData);

        for (var y = 0; y < b.Height; y++)
        for(var x = 0; x < b.Width; x++)
        {
            var pos = (y * bmpData.Stride) + (x * 4);
            var blue = pixBuff[pos];
            var green = pixBuff[pos + 1];
            var red = pixBuff[pos + 2];
            var alpha = pixBuff[pos + 3];

            if (Color.FromArgb(alpha, red, green, blue).ToArgb().Equals(targetColor.ToArgb()) &&
                new Point(x, y).Equals(targetPoint))
            {
                //execute you code here..
                MessageBox.Show("The given color exists at the given point.");
                return;
            }
        }
    }
    MessageBox.Show("The given color doesn't exist at the given point.");
}

If you want to get a list of all the positions of a given color, then create a new List<Point>() and change the check condition to:

//...
var points = new List<Point>();
if (Color.FromArgb(alpha, red, green, blue).ToArgb().Equals(targetColor.ToArgb()))
{
    points.Add(new Point(x, y));
}


来源:https://stackoverflow.com/questions/60128615/trying-to-match-pixel-colour-then-click-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!