2009-09-27 13 views
27

Ok, sto cercando una funzione o qualcosa che leggerà il colore di un determinato pixel sul mio monitor, e quando verrà rilevato quel colore, verrà abilitata un'altra funzione. Immagino di usare RGB. Tutto l'aiuto è apprezzato. Grazie.Come leggere il colore di uno schermo Pixel

+1

Si desidera monitorare l'intero desktop o un'applicazione specifica per questo cambiamento di pixel? –

+0

un certo pixel. Come dire il pixel a 125, 130 ho bisogno di aspettare fino a quando non rileva l'RGB di quel pixel andare a un certo RGB. – Brandon

+2

Perché hai bisogno di fare questo? Sembra che ci sia una qualche ragione di fondo che se ci dai un po 'più di informazioni potremmo essere in grado di darti un modo migliore per ottenere la stessa cosa. Stai scrivendo un gioco e hai bisogno di determinare quando due oggetti si scontrano? Stai cercando di capire se un certo programma è iniziato? – Lee

risposta

3

Per quanto ne so il modo più semplice per farlo è quello di:

  1. take a screenshot
  2. sguardo al bitmap e ottenere il colore del pixel

Modifica

Probabilmente non c'è modo di "aspettare" finché il pixel non cambia in un determinato colore. Probabilmente il tuo programma si limiterà a loopare e controllarlo ogni tanto fino a quando non vedrà il colore.

Ad esempio:

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

EDIT 2

Ecco alcuni esempi di codice è possibile modificare. Questo codice cambia semplicemente il colore di un'etichetta in base al colore corrente in un dato pixel. Questo codice evita la perdita di handle menzionata.

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

Preferirei non farlo in questo modo, ci ho pensato ma non è un'opzione possibile. Il pixel deve essere guardato costantemente come troverà il pixel che sta cercando in meno di un minuto, costantemente. – Brandon

+0

Quindi non dormire. Questo ciclo probabilmente impiegherebbe tutti i .1 secondi per scorrere nel peggiore dei casi. – tster

+1

Nota che non si deve correre .CopyFromScreen, ha una perdita di maniglia, il migliore è quello di implementare questo codice da soli utilizzando API Win32 –

33

Questa è la più efficiente: Si prende un pixel nella posizione del cursore, e non si basa su un solo avere 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); 
     } 
    } 
} 

Ora, ovviamente, non è necessario utilizzare la posizione corrente del cursore, ma questa è l'idea generale.

EDIT:

Ciò premesso GetColorAt funzione è possibile interrogare un certo pixel sullo schermo in una performance modo amichevole sicuro in questo modo:

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

si può avvolgere che in un thread se vuoi, o eseguilo da un'applicazione Console. "Qualunque sia la tua fantasia," suppongo.

+0

+1 per indicare che CopyFromScreen può essere usato per catturare solo una piccola area dello schermo. – tster

+0

Ma nota che CopyFormScreen perde un handle ogni volta che lo chiami. –

+2

Aggiornato per prevenire la perdita. –

4

prega di consultare questo due diverse funzioni che ho usato in uno dei miei progetti precedenti:

1) Questa funzione prende un'istantanea del desktop

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) Questa funzione prende un'immagine in input e calcola Media RGB dell'intervallo di pixel indicato.

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

Queste due funzioni potrebbero risolvere il problema. Happy Coding :)

EDIT: Si noti che GetPixel è una funzione molto lenta. Ci penserò due volte prima di usarlo.

12

La maggior parte delle risposte qui utilizza la stessa fonte di quel pixel (desktop dc).
La funzione del tasto è 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); 
} 

Penso che questo sia il modo più pulito e veloce.

Nota:

Se si è modificata la dimensione del testo di default tra le impostazioni di visualizzazione di Windows per aumentare la leggibilità su un display ad alta risoluzione, i parametri di GetPixel coordinata() devono essere regolati allo stesso modo. Ad esempio, se la posizione del cursore è (x, y) con il 150% della dimensione del testo su Windows 7, è necessario chiamare GetPixel (x * 1.5, y * 1.5) per ottenere il colore del pixel sotto il cursore.

+0

Habib, la nota che hai aggiunto non fa parte della mia risposta. Non è il mio know-how, è tuo. Per favore, fai un commento, se puoi. Quindi puoi ottenere voti positivi sul commento. Inoltre penso che regolare xey su GetPixel sia fuori luogo. È meglio controllare le regolazioni della risoluzione e la posizione del cursore da qualche parte al di fuori del metodo GetColorAt. – Bitterblue

+0

+1 per la nota sulla dimensione del testo sul display. Se c'è una dimensione del testo diversa da 100% e non è regolata correttamente, il metodo otterrà i colori dai pixel errati. –

Problemi correlati