[c# .NET] Pixelfarbe suchen

Marschal

Großer Enumerator
Hallo Community.

Ich möchte, wie der Titel es schon andeutet, meine Bildshirmausgabe nach einen bestimmten Farbwerd durchsuchen.

Dazu hatte ich mir folgendes überlegt:
C++:
while (true)
            {
...
                desktopDC = GetDC(IntPtr.Zero);

                searchc = GetPixel(desktopDC, x, y);

                ReleaseDC(IntPtr.Zero, desktopDC);
                tmp = Color.FromArgb((int)searchc);

                if (tmp.R == gesucht[0].R && tmp.G == gesucht[0].G && tmp.B == gesucht[0].B)
                {
                  //freu, farbe gefunden
                   //mach was... 
                }
       //x und y sinnvoll erhöhen...
dan man die Ausführung dieser Funktion nicht sieht, habe ich noch den Cursor an die jeweilige Stelle x/y gesetzt. Dabei stellt man fest, das die Funktion sowas von lahm ist, dass die nix taugt.
Habe ich was falsch gemacht, oder gibts es generell bessere Lösungen, den Bildschirm nach bestimmten Farben zu durchsuchen?

Danke im Voraus, Marschal
 
Zuletzt bearbeitet von einem Moderator:
Hi

Ist ein bisschen eingeschränkt was du an Code preis gibst. Was machen GetDC, GetPixel, ReleaseDC? Was ist unter "sinnvollem erhöhen" zu verstehen? Poste doch mal den kompletten Code!
 
GetDc ReleaseDC und GetPixel siehe Win32 API, das sind Funktionen, die mit iterop eingebaut werden. sinvolles erhöhen bedeutet, dass die x und y werte in einer schleife so erhöht weren, das beispielsweise, der ganze bildschirm, also jedes Pixel auf den richtigen Farbwert untersucht wird:)
 
Hallo,

musst du wirklich bei jedem durchlauf Get- und ReleaseDC aufrufen? Kannst du nicht dein Device holen, arbeitest die Schleife ab und gibts es am ende wieder frei? Somit reduzierst du die Aufrufe um ein vielfaches, welches dir auch einen enormen Performanceschub bringen sollte. Desweiteren ist GetPixel wissentlich sehr langsam. Such mal nach "Marshal.Copy und BitmapData.LockBits" (hab jetzt leider kein Codebeispiel zur Hand), vielleicht kannst du damit etwas anfangen. Ich weiß bei Bildern kannst du so die Farbinformationen in ein byte Array oder IntPtr kopieren. Du musst natürlich daruf achten was du für eine Farbtiefe hast, damit du das byte array richtig interpretierst.

Vielleicht schaff ich es heute abend mal noch ein Codebeispiel raus zu suchen.

Gruß
 
GetDc ReleaseDC und GetPixel siehe Win32 API, das sind Funktionen, die mit iterop eingebaut werden. sinvolles erhöhen bedeutet, dass die x und y werte in einer schleife so erhöht weren, das beispielsweise, der ganze bildschirm, also jedes Pixel auf den richtigen Farbwert untersucht wird:)

Hi

Und da wollte ich eben den Code haben, wie du X und Y weitersetzt. Zur Zeit sehe ich nur eine Schleife While(true), die nichtssagend ist, die Steuerung liegt aber irgendwo versteckt.
Bitte posten den kompletten Code!

Oder kannst du mir sagen, warum das lange dauert? ;)
C#:
while(true)
{
    // do something
    Console.WriteLine("gib was aus");
    // do something else
}
 
@Nico Graichen: die While.Schleife ist doch komplett wurscht;) Elegant wäre eine gaschalelte for schleife, meinen Bildschirm wie eine 1400x900 Matrix absucht;) also ist es absolut uninteressant, wie x und y hochgezählt werden. Es liegt wirklich daran, dass die etwas veralteten Funktionen aus der Win API zu lange brauchen;)
die Funktionen sind hier zu begutachten:

GetDC, ReleaseDC, GetPixel.

Das sind die drei Funktionen, die extern geladen werden. (iterop).

Wie das passiert, dolg hier:
C++:
        [DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hwnd);
        [DllImport("user32.dll")]
        static extern Int32 ReleaseDC(IntPtr hwnd, IntPtr hdc);
        [DllImport("Gdi32.Dll")]
        static extern uint GetPixel(IntPtr hdc, int nXPos, int nYPos);

zu der Schleife. Angenommen, ich möchte die obersten 100x 100 Pixel untersuchen, dann nehme ich meine Whileschleife, und lasse am ende der Schleife x und y hochzählen. Das könnte dan so aussehen (rein exemplarisch):
C++:
while (true)
            {
                desktopDC = GetDC(IntPtr.Zero);
                searchc = GetPixel(desktopDC, x, y);
//x und y ist die Position, also das Pixel, welches grade untersucht wird...
                ReleaseDC(IntPtr.Zero, desktopDC);
                tmp = Color.FromArgb((int)searchc);

//ist vom Typ uint und tmp ist vom Typ Color

                if (tmp.R == gesucht[0].R && tmp.G == gesucht[0].G && tmp.B == gesucht[0].B)
                {
//gesucht[] ist ein Array, mit den passenden RGB Farbwerten, nach denen gesucht wird...
                    doSomething("blub");
//wenn gefunden, mach irgendwas
//Abbruchbedingung..
                }
                if (y >= 0)
                {
                   y++;
                }
                if (x >= 0)
                {
                   x++;
                }
                if (y >= 100)
                {
                   y = 0
                    x += 1;
                }
                if (x >= 100)
                {
                    x = 0;
                    y += 1;
                }
//an dieser stelle ist eine geschachtelte forschlafe eleganter, aber es geht a schlieslich nicht um die Schleife;)        

            }

So, hier der Code ausschnitt.

@napstermania: Es wäre sehr nett, wenn du mal nen codesnippet posten könntest;)
Zum Device Kontext, ich habe auch versucht, erst den Device zu schliesen, wenn ich einmal durch bin, also meinen suchbereich komplett durchsucht hab, doch das hat nicht funktioniert. der ist dan über den gesuchten Farbwert drüber und hat nüscht gefunden:(

MfG und Danke für eure Hilfe:) Hoffe jetzt ist es klarer geworden, was ich mache und was hier nicht stimmt:)
 
Zuletzt bearbeitet von einem Moderator:
Ich weiß nicht ob dir das was hilft, aber meinen ganzen Bildschirm mit GetPixel abzutasten dauert bei mir so um die vier Sekunden. Wenn ich aber vorher einen Screenshot mache (wie hier für VB.Net gezeigt) und dann die GetPixel-Methode der Bitmap-Klasse benutze, so dauert es nur noch ungefähr zwei Sekunden. Habe es auf WinXP getestet.
 
Danke, das werd ich mal ausprobieren, VB.NET ist ja leicht zu übersetzen;)

PS: habe übrigends Win7. Also meine Variante wie oben gepostet, da schlaf ich ein beim zusehen:( Könntest du mal ein Beispiel von dir posten, bitte?
 
Zuletzt bearbeitet:
Konsolenanwendung:
Code:
Option Strict On
Imports System.Drawing
Imports System.Windows.Forms
Module Module1
    Sub Main()
        Dim sw As Stopwatch = Stopwatch.StartNew
        Using b As Bitmap = CaptureScreen()
            For x As Integer = 0 To b.Width - 1
                For y As Integer = 0 To b.Height - 1
                    Dim searchC As Color = b.GetPixel(x, y)
                    ' jetzt irgendwas mit der Farbe tun
                Next
            Next
        End Using
        sw.Stop()
        Console.WriteLine("Benötigte Millisekunden: " & CStr(sw.ElapsedMilliseconds))
        Console.ReadKey()
    End Sub
    Public Function CaptureScreen() As Bitmap
        Dim b As New Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height)
        Using g As Graphics = Graphics.FromImage(b)
            g.CopyFromScreen(0, 0, 0, 0, b.Size)
        End Using
        Return b
    End Function
End Module
Bei 1280*1042 Pixeln habe ich eine Laufzeit von ca. 2,3 Sekunden
 
Hey, cool, Danke;) Es läuft wirklich viel viel schneller. Da liegen ja Welten zwischen deinem Vorschlag, und der GetPixel Methode von mir:D
Noch mal danke.
Habe hier noch mal den Code in C# .NET überführt:
C++:
Color testC = Color.FromArgb(0, 0, 255);
//testC ist unsere gesuchte Farbe:)
            System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
    using (Bitmap b = CaptureScreen()) {
      for (int x = 0; x <= b.Width - 1; x++) {
        for (int y = 0; y <= b.Height - 1; y++) {
          Color searchC = b.GetPixel(x, y);
                    if (searchC == testC)
                    {
//wenn Farbe gefunden
                        Point p = new Point(x, y);
                        Cursor.Position = p;                        
                        sw.Stop();
                        MessageBox.Show("Benötigte Millisekunden: " + sw.ElapsedMilliseconds.ToString());
                        return;
                    }
        }
      }
    }
    sw.Stop();
    MessageBox.Show("Benötigte Millisekunden: " + sw.ElapsedMilliseconds.ToString());
  }
  public static Bitmap CaptureScreen()
  {
    Bitmap b = new Bitmap(SystemInformation.VirtualScreen.Width, SystemInformation.VirtualScreen.Height);
    using (Graphics g = Graphics.FromImage(b)) {
      g.CopyFromScreen(0, 0, 0, 0, b.Size);
    }
    return b;
        }               
    }
 
Zuletzt bearbeitet von einem Moderator:
Zurück