Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[C# Multi-Threaded] Screenshots / bitmap rendering

Pagina: 1
Acties:

  • evolution536
  • Registratie: Maart 2009
  • Laatst online: 05-06-2024
Hallo allemaal! In deze thread zou ik een vraag willen stellen op basis van mening en suggesties / ideeën waar ik mogelijk niet over nagedacht heb. Ik ben bezig met het bouwen van een programma en dat programma doet het volgende.

Er draait een applicatie in window mode. Mijn programma maakt continue screenshots van dit venster, loopt door de pixels heen en kijkt of de kleur van de pixel matcht met de geconfigureerde kleur. Als de kleur matcht dan verplaatst het programma de cursor naar deze pixel.

Dit stuk code is zeer performance kritisch. De eerste methode die ik hanteerde was de simpelste, met GetPixel. GetPixel was echter veel te langzaam bij het snel renderen van screenshots en gebruikte ook veel te veel processorkracht. Nu heb ik de manier veranderd in het doorlopen van de RGB waardes van de bitmap, wat 2 tot 2,5 keer zo snel is. Daar ben ik nu tevreden over. Echter vind ik het een uitdaging om het nog sneller te krijgen. Ik wil ervoor zorgen dat meerdere threads tegelijk werken om deze operatie sneller te laten werken. De cursor moet zo soepel mogelijk worden verplaatst, wat deze code moet verzorgen ;)

Ik heb nagedacht over twee mogelijkheden:
- Ik maak meerdere threads die allemaal een aparte screenshot maken en renderen. Het voordeel hiervan is dat het gemakkelijker te implementeren is. Het nadeel is dat ik goed moet bedenken hoe ik ervoor kan zorgen dat de threads niet helemaal gelijk lopen. Als ze gelijk lopen betekent het dat ze dezelfde screenshot maken, wat overbodig werk is, maar ook de cursor de verkeerde kant op zou kunnen sturen.
- Ik splits de RGB data van de bitmap in meerdere delen, waarna iedere thread zijn eigen deel rendert. Het voordeel is dat er steeds maar één screenshot wordt gemaakt, dus dat het hierboven beschreven probleem niet zal voorkomen. Het nadeel is dat het moeilijker te implementeren is, en dat ik moet uitzoeken hoe ik ervoor kan zorgen dat alle threads van elkaar weten of er al een pixel is gevonden die matcht. Als ik dit niet doe kan het voorkomen dat meerdere threads een pixel vinden, wat er voor gaat zorgen dat de cursor over verschillende plekken op het scherm gaat springen.

Zouden er nog andere manieren zijn om met gebruik van meerdere threads een programma als dit nog sneller te laten draaien? Ik ben aan het proberen te bedenken hoe ik dit zou kunnen oplossen maar tot nog toe ben ik niet verder gekomen dan deze twee oplossingen. Ook heb ik nog geen idee over hoe ik de beschreven problemen zou kunnen oplossen in code.

  • Gropah
  • Registratie: December 2007
  • Niet online

Gropah

Admin Softe Goederen

Oompa-Loompa 💩

Ik weet weinig van C#, maar is het niet mogelijk om 1 class te maken die de screenshot neemt, hem vervolgens in x aantal delen opsplits en vervolgens x aantal threads start met elke een eigen onderdeel. Op het moment dat 1 thread 1 pixel vind die voldoet aan jouw eis, returnt hij de locatie naar de eerste class, die de rest van de threads stopt en vervolgens die ene pixel gebruikt om de muis naar die locatie te verplaatsen.

  • Haan
  • Registratie: Februari 2004
  • Laatst online: 16:52

Haan

dotnetter

Ik heb geen antwoord op je specifieke probleem, maar (er vanuit gaande dat je minimaal .NET 4 gebruikt) de Task Parallel Library gaat je grote vriend zijn hierbij :)
De link bevat sowieso een hoop interessante informatie over do's en dont's bij parallel programmeren.

Kater? Eerst water, de rest komt later


  • RobIII
  • Registratie: December 2001
  • Niet online

RobIII

Admin Devschuur®

^ Romeinse Ⅲ ja!

(overleden)
Ik zou eens beginnen met de bekijken of 't niet anders kan dan met 't gebruik van screenshots; dat is namelijk an-sich al niet heel erg performant. Is er geen betere manier om (een regio van wat je te pakken probeert) te verkrijgen wat je zoekt? Kun je iets specifieker zijn over wat je precies probeert te bereiken?

Om maar eens een nadeeltje te noemen: hoe gaat je applicatie overweg met mijn triple-monitor setup van 3x1920x1080? ;)

Ook belangrijk is de "target"; is die 1x1 pixel? Of 10x10 pixels of...? Want als je target 1x1 pixel is zul je op je GetPixel manier (met een wat naïve implementatie) in principe elke pixel moeten scannen in een constructie a-la:
code:
1
2
3
4
5
for x = 0 to resolution_width {
  for y = 0 to resolution_height {
    if getPixel(x, y) == mycolor then ...
  }
}

Terwijl je met een target van, zeg, 10x10 al zoiets zou kunnen doen:
code:
1
2
3
4
5
for x = 0 to resolution_width step 10 {
  for y = 0 to resolution_height step 10 {
    if getPixel(x, y) == mycolor then ...
  }
}

Wat al 100x beter performt bij 't zoeken van de juiste target.

(Zoals ik al zeg: dat is nogal een naïeve implementatie; je kunt je bitmap waarschijnlijk beter behandelen als een grote blob data en er wat matrix-bewerkingen op los laten om de juiste kleuren eruit te filteren e.d.)

[ Voor 65% gewijzigd door RobIII op 27-02-2013 10:23 ]

There are only two hard problems in distributed systems: 2. Exactly-once delivery 1. Guaranteed order of messages 2. Exactly-once delivery.

Je eigen tweaker.me redirect

Over mij


  • CMG
  • Registratie: Februari 2002
  • Laatst online: 10-12-2024

CMG

Als je nou eens met de RGB data als bytes gaat werken, dan ben je makkelijk een factor 50-100 sneller dan met getpixel...

Zie MSDN: Bitmap.LockBits Method (Rectangle, ImageLockMode, PixelFormat) (System.Drawing) om mee te beginnen

NKCSS - Projects - YouTube


Verwijderd

je zou een soort master class kunnen gebruiken die verschillende threads heeft lopen van de class die daadwerkelijk het zoekwerk uitvoert, de masterclass zou dan kunnen timen dat er elke x ms een volgend thread gefired wordt. Je zou ook kunnen zorgen dat de uitvoerende threads weer een event afvuren zodra de screencap genomen is waardoor de masterclass weet dat hij de volgende aan het werk kan zetten

  • timjuan
  • Registratie: November 2012
  • Laatst online: 07-05 14:06
Er zijn verschillende manieren om screenshots te maken en vervolgens de data te lezen. Wellicht kun je de code plaatsen die je gebruikt voor het maken van een screenshot en het vinden van die kleurwaarden?

  • armageddon_2k1
  • Registratie: September 2001
  • Laatst online: 27-07 10:18
Ben jij een aimbot aan het maken oid? :+

Engineering is like Tetris. Succes disappears and errors accumulate.


  • evolution536
  • Registratie: Maart 2009
  • Laatst online: 05-06-2024
Dit is een stuk code van StackOverflow dat ik heb gebruikt. Mijn eigen code staat inmiddels vol met allerlei stukken commentaar die de code onmogelijk groot maken, dus vandaar post ik deze. In feite gebruik ik gewoon dit stuk, alleen dan in 32-bit formaat in plaats van 24 dus dit is in feite gelijk aan de code die ik gebruik.

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
Bitmap bmp = new Bitmap("SomeImage");

// Lock the bitmap's bits.  
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);

// Get the address of the first line.
IntPtr ptr = bmpData.Scan0;

// Declare an array to hold the bytes of the bitmap.
int bytes = bmpData.Stride * bmp.Height;
byte[] rgbValues = new byte[bytes];
byte[] r = new byte[bytes / 3];
byte[] g = new byte[bytes / 3];
byte[] b = new byte[bytes / 3];

// Copy the RGB values into the array.
Marshal.Copy(ptr, rgbValues, 0, bytes);

int count = 0;
int stride = bmpData.Stride;

for (int column = 0; column < bmpData.Height; column++)
{
    for (int row = 0; row < bmpData.Width; row++)
    {
        b[count] = (byte)(rgbValues[(column * stride) + (row * 3)]) 
        g[count] = (byte)(rgbValues[(column * stride) + (row * 3) + 1]);
        r[count++] = (byte)(rgbValues[(column * stride) + (row * 3) + 2]);
    }
}


Zoals te zien gebruik ik al de Lockbits methode om te itereren door de kleurwaarden. Ik was er zoals in de OP ook te lezen al achter dat de GetPixel manier erg traag was.

Even voor de duidelijkheid: de bitmap die uiteindelijk geitereerd wordt is al een capture van het venster van het programma in kwestie. Het is GEEN screenshot van het hele desktop/scherm! Ik doe dit met GetWindowRect en Graphics.CopyFromScreen().

Even wat ik nu als extra oplossing zie uit de huidige posts:

Een extra class maakt de screenshot en beheert de threads. Op het moment dat de screenshot is gemaakt worden de andere threads aan het werk gezet en wanneer een positie is gevonden zal de extra class de threads stoppen. Dit lijkt me in ieder geval zeer duidelijk! Ik zal hier is even naar kijken :) Als er nog andere ideeën zijn om dit op te lossen dan hoor ik dat natuurlijk graag. Ik ben zeer geinteresseerd in nieuwe manieren om problemen op te lossen. Dankjewel in ieder geval voor de hulp tot nu toe :)

  • timjuan
  • Registratie: November 2012
  • Laatst online: 07-05 14:06
http://www.codeproject.co...e-using-DirectX-in-Csharp

Probeer die methode maar eens. Je kunt de pixel data rechtstreeks uit de buffer lezen. Dat is veel sneller dan eerst een Bitmap object maken en de buffer data daar naartoe kopieren. Als je alleen wilt zoeken naar bepaalde kleurwaarden hoeft dat allemaal niet. Als je het plaatje ook nog wilt opslaan dan maakt het niet zoveel uit.
Pagina: 1