如何阅读屏幕像素的颜色

时间:2009-09-27 16:51:03

标签: c# winforms gdi+ pixel

好的,我正在寻找能够读取显示器上某个像素颜色的功能或某些功能,当检测到该颜色时,将启用另一个功能。我想用RGB。所有帮助赞赏。谢谢。

6 个答案:

答案 0 :(得分:39)

这是最有效的:它抓取光标位置的像素,而不依赖于只有一个显示器。

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

namespace FormTest
{
    public partial class Form1 : Form
    {
        [DllImport("user32.dll")]
        static extern bool GetCursorPos(ref Point lpPoint);

        [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);

        public Form1()
        {
            InitializeComponent();
        }

        private void MouseMoveTimer_Tick(object sender, EventArgs e)
        {
            Point cursor = new Point();
            GetCursorPos(ref cursor);

            var c = GetColorAt(cursor);
            this.BackColor = c;

            if (c.R == c.G && c.G < 64 && c.B > 128)
            {
                MessageBox.Show("Blue");
            }
        }

        Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
        public Color GetColorAt(Point location)
        {
            using (Graphics gdest = Graphics.FromImage(screenPixel))
            {
                using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
                {
                    IntPtr hSrcDC = gsrc.GetHdc();
                    IntPtr hDC = gdest.GetHdc();
                    int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
                    gdest.ReleaseHdc();
                    gsrc.ReleaseHdc();
                }
            }

            return screenPixel.GetPixel(0, 0);
        }
    }
}

现在,显然,您不必使用光标的当前位置,但这是一般的想法。

修改

鉴于上述GetColorAt功能,您可以安全,性能友好的方式轮询屏幕上的某个像素,如下所示:

private void PollPixel(Point location, Color color)
{
    while(true)
    {
        var c = GetColorAt(location);

        if (c.R == color.R && c.G == color.G && c.B == color.B)
        {
            DoAction();
            return;
        }

        // By calling Thread.Sleep() without a parameter, we are signaling to the
        // operating system that we only want to sleep long enough for other
        // applications.  As soon as the other apps yield their CPU time, we will
        // regain control.
        Thread.Sleep()
    }
}

如果需要,可以将其包装在线程中,或者从控制台应用程序执行它。 “不管你喜欢什么,”我想。

答案 1 :(得分:15)

这里的大多数答案都使用该像素的相同来源(桌面直流) 关键功能是GetPixel

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr window);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern uint GetPixel(IntPtr dc, int x, int y);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr window, IntPtr dc);

public static Color GetColorAt(int x, int y)
{
    IntPtr desk = GetDesktopWindow();
    IntPtr dc = GetWindowDC(desk);
    int a = (int) GetPixel(dc, x, y);
    ReleaseDC(desk, dc);
    return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff);
}

我认为这是最干净,最快捷的方式。

注意

如果您在Windows上的“显示设置”中修改了默认文本大小以提高高分辨率显示屏的可读性,则需要以相同方式调整GetPixel()的坐标参数。例如,如果光标位置为(x,y),在Windows 7上具有150%的文本大小,则需要调用GetPixel(x * 1.5,y * 1.5)以获取光标下像素的颜色。

答案 2 :(得分:5)

请检查我在之前的一个项目中使用的这两个不同的功能:

1)此功能拍摄桌面

的快照
private void CaptureScreenAndSave(string strSavePath)
        {

            //SetTitle("Capturing Screen...");

            Bitmap bmpScreenshot;

            Graphics gfxScreenshot;
            bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            gfxScreenshot = Graphics.FromImage(bmpScreenshot);
            gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
            MemoryStream msIn = new MemoryStream();
            bmpScreenshot.Save(msIn, System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[0], null);

            msIn.Close();

            byte[] buf = msIn.ToArray();

            MemoryStream msOut = new MemoryStream();

            msOut.Write(buf, 0, buf.Length);

            msOut.Position = 0;

            Bitmap bmpOut = new Bitmap(msOut);

            try
            {
                bmpOut.Save(strSavePath, System.Drawing.Imaging.ImageFormat.Bmp);
                //SetTitle("Capturing Screen Image Saved...");
            }

            catch (Exception exp)
            {

            }

            finally
            {
                msOut.Close();
            }
        }

2)此功能在输入中拍摄图像并计算给定的像素范围的RGB平均值。

double GetRGBAverageForPixelRange( int istartRange, int iEndRange,  Bitmap oBitmap )
        {
            double dRetnVal = 0 ;
            Color oTempColor ; 
            int i, j ;
            for( int iCounter = istartRange ; iCounter < iEndRange ; iCounter++ )
            {
                i = (iCounter % (oBitmap.Width));
                j = ( iCounter / ( oBitmap.Width ) ) ;
                if (i >= 0 && j >= 0 && i < oBitmap.Width && j < oBitmap.Height )
                {
                    oTempColor = oBitmap.GetPixel(i, j);
                    dRetnVal = dRetnVal + oTempColor.ToArgb();
                }

            }
            return dRetnVal ;
        }

这两个功能可以解决您的问题。快乐编码:)

编辑:请注意,GetPixel的功能非常慢。我会三思而后行。

答案 3 :(得分:5)

此函数较短,使用System.Drawing可以获得相同的结果,没有Pinvoke。

Bitmap bmp = new Bitmap(1, 1);
Color GetColorAt(int x, int y)
{
    Rectangle bounds = new Rectangle(x, y, 1, 1);
    using (Graphics g = Graphics.FromImage(bmp))
        g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
    return bmp.GetPixel(0, 0);
}

答案 4 :(得分:4)

据我所知,最简单的方法是:

  1. take a screenshot
  2. 查看位图并获取像素颜色
  3. 修改

    在像素变为某种颜色之前,可能无法“等待”。你的程序可能只需要经常循环并经常检查它,直到它看到颜色。

    例如:

    while(!IsPixelColor(x, y, color))
    {
        //probably best to add a sleep here so your program doesn't use too much CPU
    }
    DoAction();
    

    编辑2

    以下是您可以修改的示例代码。此代码仅根据给定像素中的当前颜色更改标签的颜色。此代码避免了提到的句柄泄漏。

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    using System.Runtime.InteropServices;
    
    namespace WindowsFormsApplication1
    {
    public partial class Form1 : Form
    {
    
        [DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
    
    
        Thread t;
        int x, y;
    
        public Form1()
        {
            InitializeComponent();
        }
    
        private void Form1_Load(object sender, EventArgs e)
        {
            x = 20;
            y = 50;
            t = new Thread(update);
            t.Start();
        }
    
        private void update()
        {
            Bitmap screenCopy = new Bitmap(1, 1);
            using (Graphics gdest = Graphics.FromImage(screenCopy))
            {
                while (true)
                {
                    //g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(256, 256));
                    using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
                    {
                        IntPtr hSrcDC = gsrc.GetHdc();
                        IntPtr hDC = gdest.GetHdc();
                        int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, x, y, (int)CopyPixelOperation.SourceCopy);
                        gdest.ReleaseHdc();
                        gsrc.ReleaseHdc();
                    }
                    Color c = Color.FromArgb(screenCopy.GetPixel(0, 0).ToArgb());
                    label1.ForeColor = c;
                }
            }
        }
    }
    

    }

答案 5 :(得分:1)

这条线使用了大约 10 毫秒。

int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);