2009-09-27 6 views
27

De acuerdo, estoy buscando una función o algo que lea el color de un determinado píxel en mi monitor, y cuando se detecte ese color, se habilitará otra función. Me imagino usando RGB. Toda ayuda apreciada. Gracias.Cómo leer el color de un Pixel de pantalla

+1

¿Desea supervisar todo el escritorio o una aplicación específica para este cambio de píxeles? –

+0

un cierto píxel. Como decir el píxel en 125, 130, necesito esperar hasta que detecte que el RGB de ese píxel va a un cierto RGB. – Brandon

+2

¿Por qué necesita hacer esto? Parece que hay algún tipo de razón subyacente por la que, si nos proporciona un poco más de información, podríamos ofrecerle una mejor manera de lograr lo mismo. ¿Estás escribiendo un juego y necesitas determinar cuándo chocan dos objetos? ¿Estás tratando de averiguar si un determinado programa ha comenzado? – Lee

Respuesta

3

Por lo que yo sé la manera más fácil de hacer esto es:

  1. take a screenshot
  2. vistazo al mapa de bits y obtener el color del píxel

Editar

Probablemente no haya forma de "esperar" hasta que el píxel cambie a un determinado color. Su programa probablemente tendrá que repetir y revisarlo de vez en cuando hasta que vea el color.

Por ejemplo:

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

EDIT 2

Aquí hay un código ejemplo que puede modificar. Este código simplemente cambia el color de una etiqueta en función del color actual en un píxel determinado. Este código evita la pérdida de identificador mencionada.

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; 
      } 
     } 
    } 
} 

}

+0

Prefiero no hacerlo de esta forma, lo he pensado, pero simplemente no es una opción. El píxel debe vigilarse constantemente, ya que encontrará el píxel que busca en menos de un minuto, constantemente. – Brandon

+0

Entonces no duermas. Este ciclo probablemente tomaría todos los segundos .1 para recorrer el peor de los casos. – tster

+1

Tenga en cuenta que no debe ejecutar .CopyFromScreen, tiene una fuga de controlador, lo mejor es implementar este código usted mismo con Win32 API –

33

Este es el más eficiente: Se agarra a un pixel en la posición del cursor, y no se basa en tener sólo un monitor.

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); 
     } 
    } 
} 

Ahora, obviamente, no tienen que utilizar la ubicación actual del cursor, pero esta es la idea general.

EDIT:

Dada la GetColorAt función anterior puede sondear un determinado píxel en la pantalla de un modo seguro y agradable, el rendimiento de esta manera:

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() 
    } 
} 

se puede envolver que en un hilo si lo desea, o ejecútelo desde una aplicación de consola. "Lo que sea que te guste", supongo.

+0

+1 para señalar que CopyFromScreen se puede usar para capturar un área pequeña de la pantalla. – tster

+0

Pero tenga en cuenta que CopyFormScreen perderá un identificador cada vez que lo llame. –

+2

Actualizado para evitar la fuga. –

4

Selecciona esta dos funciones diferentes que he utilizado en uno de mis proyectos anteriores:

1) Esta función toma instantánea de escritorio

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) Esta función toma una imagen de entrada y calcula Promedio de RGB del rango de píxeles dado.

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 ; 
     } 

Estas dos funciones juntas pueden resolver su problema. Happy Coding :)

EDIT: Tenga en cuenta que GetPixel es una función muy lenta. Lo pensaré dos veces antes de usarlo.

12

La mayoría de las respuestas aquí utilizan la misma fuente de ese píxel (PC de escritorio).
La función clave es 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); 
} 

Creo que esta es la forma más limpia y rápida.

Nota:

Si ha modificado el tamaño de texto predeterminado entre la configuración de pantalla en Windows para aumentar la legibilidad en una pantalla de alta resolución, los parámetros de GetPixel de coordenadas() que haya que ajustar la misma manera. Por ejemplo, si la ubicación del cursor es (x, y) con un 150% del tamaño del texto en Windows 7, debe llamar a GetPixel (x * 1.5, y * 1.5) para obtener el color del píxel debajo del cursor.

+0

Habib, la nota que agregó no es parte de mi respuesta. No es mi know-how, es tuyo. Por favor, haz un comentario, si puedes. Luego puedes obtener votos por adelantado sobre el comentario. Además, creo que ajustar x e y en GetPixel está fuera de lugar. Es mejor verificar los ajustes de resolución y la posición del cursor en algún lugar fuera del método GetColorAt. – Bitterblue

+0

+1 para la nota sobre el tamaño del texto en la pantalla. Si hay un tamaño de texto diferente al 100% y no está ajustado correctamente, el método obtendrá colores de los píxeles incorrectos. –

Cuestiones relacionadas