Pixel Counter

Go to solution Solved by Nine,

Recommended Posts

Hey guys,

is there a simple way to count the number of coloured pixels (for example green) in an area ?

e.g. Pos 1: 500x 500y to Pos 2: 600x to 600y

PixelCount ???

THX

Share on other sites
9 hours ago, Dexter1993 said:

PixelCount ???

Not a beauty, but may be sufficient .

EDIT : new version

```#include <Timers.au3>
#include <MsgBoxConstants.au3>

HotKeySet("{ESC}", "_Terminate")
HotKeySet("+!1", "_PixelCount1") ; Shift-Alt-1 ==> start search using PixelGetColor
HotKeySet("+!2", "_PixelCount2") ; Shift-Alt-2 ==> start search using PixelSearch

Global \$iLeft, \$iTop, \$iRight, \$iBottom ; coordinates of a rectangle
Global \$sColor, \$iVariation, \$iChecked, \$iFound
; set values :
\$iLeft      = 0
\$iTop       = 0
\$iRight     = 200
\$iBottom    = 200
\$sColor     = "FFFF00" ; color you are looking for
\$iVariation = 0
\$iChecked   = 0 ; Pixels checked
\$iFound     = 0 ; Pixels found

While True
Sleep(100)
WEnd

; 1. using PixelGetColor :
Func _PixelCount1()
Local \$iMsgID
Local \$hStarttime = _Timer_Init()

ConsoleWrite("Process running ..." & @CRLF)
\$iChecked   = 0
\$iFound     = 0
For \$x = \$iLeft To \$iRight
For \$y = \$iTop To \$iBottom
If (Hex(PixelGetColor(\$x, \$y), 6)) = \$sColor Then
\$iChecked += 1
\$iFound   += 1
Else
\$iChecked += 1
EndIf
Next
Next

ConsoleWrite("Process finished" & @CRLF)
\$iMsgID = MsgBox(BitOR(\$MB_SYSTEMMODAL, \$MB_ICONINFORMATION, \$MB_RETRYCANCEL), _
"PixelGetColor Result :", _
"Pixels checked : " & @CRLF & \$iChecked & @CRLF & _
"Pixels found   : " & @CRLF & \$iFound   & @CRLF & @CRLF & _
"Time           : " & @CRLF & _Timer_Diff(\$hStarttime) & " ms" & @CRLF)
If \$iMsgID = \$IDCANCEL Then Exit
EndFunc

; 2. using PixelSearch :
Func _PixelCount2()
Local \$iMsgID, \$xColor
Local \$hStarttime = _Timer_Init()

ConsoleWrite("Process running ..." & @CRLF)
\$iChecked   = 0
\$iFound     = 0
For \$x = \$iLeft To \$iRight
For \$y = \$iTop To \$iBottom
PixelSearch(\$x, \$y, \$x, \$y, Dec(\$sColor), \$iVariation)
If @error Then
\$iChecked += 1
Else
\$iFound   += 1
\$iChecked += 1
EndIf
Next
Next

ConsoleWrite("Process finished" & @CRLF)
\$iMsgID = MsgBox(BitOR(\$MB_SYSTEMMODAL, \$MB_ICONINFORMATION, \$MB_RETRYCANCEL), _
"PixelSearch Result :", _
"Pixels checked : " & @CRLF & \$iChecked & @CRLF & _
"Pixels found   : " & @CRLF & \$iFound   & @CRLF & @CRLF & _
"Time           : " & @CRLF & _Timer_Diff(\$hStarttime) & " ms" & @CRLF)
If \$iMsgID = \$IDCANCEL Then Exit
EndFunc

Func _Terminate()
MsgBox(BitOR(4096, 64), "Message :", "Script terminated" & @CRLF)
Exit
EndFunc```

Edited by Musashi
new version

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Share on other sites

You can use my Screen Scraping UDF (see signature).  Would be lot faster especially on Win10.

Not much of a signature but working on it...

Spoiler

Share on other sites

@Nine thx man. Very nice UDF!

but can you show me a simple example for this sitation:

Thx !

Share on other sites
• Solution
8 minutes ago, Dexter1993 said:

Very nice UDF!

Thank you.  The simplest way would be to use @Musashi 1st example but instead of calling PixelGetColor, you would use _GetScreen_GetPixel.

For an even faster way, you can also use _GetScreen_SearchArea repeatedly with the start information set at each loop until you get an error.

Put a script together and if you have issues, I will be glad to help you out.

Not much of a signature but working on it...

Spoiler

Share on other sites

hm... i dont know bro  i test it the last 2 hours. My desktop is full of tests

```#include <MsgBoxConstants.au3>
#include <Constants.au3>
#include "GetScreen.au3"
#include <Array.au3>

HotKeySet("{ESC}", "_PixelCount1")

Global \$iLeft, \$iTop, \$iRight, \$iBottom ; coordinates of a rectangle
Global \$sColor, \$iVariation, \$iChecked, \$iFound
; set values :
\$iLeft      = 0
\$iTop       = 0
\$iRight     = 600
\$iBottom    = 600
\$sColor     = 0xD05E5E ; color you are looking for
\$iVariation = 0
\$iChecked   = 0 ; Pixels checked
\$iFound     = 0 ; Pixels found

_PixelCount1()

Func _PixelCount1()

\$iChecked   = 0
\$iFound     = 0
For \$x = \$iLeft To \$iRight
For \$y = \$iTop To \$iBottom
If (Hex(_GetScreen_GetPixel(\$x, \$y), 6)) = \$sColor Then
\$iChecked += 1
\$iFound   += 1
Else
\$iChecked += 1
EndIf
Next
Next

MsgBox (1,"","Pixels checked :" & \$iChecked & " Pixel found: " & \$iFound)

EndFunc```

I test it with a red point.

Edited by Dexter1993
Share on other sites

The \$iChecked counter in the loop is not needed, it will always be (\$iRight-\$iLeft)*(\$iBottom-\$iTop).

Some guy's script + some other guy's script = my script!

Share on other sites

Look at examples in the UDF.  You need to initialize the buffer and then read the screen.  You are not doing it properly.  Take a moment to understand what you are doing...

Not much of a signature but working on it...

Spoiler

Share on other sites
1 hour ago, Dexter1993 said:

hm... i dont know bro  i test it the last 2 hours. My desktop is full of tests

Try :

```#include <Timers.au3>
#include <MsgBoxConstants.au3>
#include <Constants.au3>
#include "GetScreen.au3"
#include <Array.au3>
Opt("MustDeclareVars", True)

HotKeySet("{ESC}", "_Terminate")
HotKeySet("+!1", "_PixelCount1") ; Shift-Alt-1 ==> start search with _GetScreen_GetPixel

Global \$iLeft, \$iTop, \$iRight, \$iBottom ; coordinates of a rectangle
Global \$sColor, \$iFound
; set values :
\$iLeft      = 0
\$iTop       = 0
\$iRight     = 600
\$iBottom    = 600
\$sColor     = "D05E5E" ; color you are looking for
\$iFound     = 0 ; Pixels found

While True
Sleep(100)
WEnd

Func _PixelCount1()
Local \$iMsgID
Local \$hStarttime = _Timer_Init()
\$iFound  = 0

_GetScreen_Initialize((\$iRight - \$iLeft), (\$iBottom - \$iTop))
_GetScreen_GetScreen()
For \$x = \$iLeft To \$iRight
For \$y = \$iTop To \$iBottom
If (Hex(_GetScreen_GetPixel(\$x, \$y), 6)) = \$sColor Then
\$iFound   += 1
EndIf
Next
Next

\$iMsgID = MsgBox(BitOR(\$MB_SYSTEMMODAL, \$MB_ICONINFORMATION, \$MB_RETRYCANCEL), _
"PixelSearch Result :", _
"Pixels checked : " & @CRLF & (\$iRight-\$iLeft)*(\$iBottom-\$iTop) & @CRLF & _
"Pixels found   : " & @CRLF & \$iFound   & @CRLF & @CRLF & _
"Time           : " & @CRLF & _Timer_Diff(\$hStarttime) & " ms" & @CRLF)
If \$iMsgID = \$IDCANCEL Then Exit
EndFunc

Func _Terminate()
MsgBox(BitOR(\$MB_SYSTEMMODAL, \$MB_ICONINFORMATION), "Message :", "Script terminated" & @CRLF)
Exit
EndFunc```

EDIT :

As already explained by @Nine : "You need to initialize the buffer and then read the screen."

Edited by Musashi

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Share on other sites

Besides, a nice UDF

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Share on other sites

The UDF always starts at 0,0 from the top left, you can reduce the read to a lower right bottom but it will save only micro secs.  Better read the whole screen. Then you can search for specific area.

Not much of a signature but working on it...

Spoiler

Share on other sites
7 minutes ago, Musashi said:

Besides, a nice UDF

Thanks.  I am using it a lot, and I know it is working very well.  Sometimes, you need pixel driven script to ease the complexity of a tool.

Not much of a signature but working on it...

Spoiler

Share on other sites

THX guys! Very Helpfull.
I have now adapted it the way I need it

thx @Nine !!!!

thx @Musashi

```#include "GetScreen.au3"

MsgBox (1,"Founded pixel",_PixelCountInArea(0,0,500,500,"D05E5E"))

Func _PixelCountInArea(\$left,\$top,\$right,\$bottom,\$color)

\$founded  = 0

_GetScreen_Initialize((\$right - \$left), (\$bottom - \$top))
_GetScreen_GetScreen()

For \$x = \$left To \$right
For \$y = \$top To \$bottom
If (Hex(_GetScreen_GetPixel(\$x, \$y), 6)) = \$color Then
\$founded += 1
EndIf
Next
Next

Return \$founded
EndFunc```

Share on other sites

Initialize should say right and bottom, as I said the UDF always starts at 0,0

So code should be :

```#include "GetScreen.au3"

MsgBox (1,"Founded pixel",_PixelCountInArea(0,0,500,500,"D05E5E"))

Func _PixelCountInArea(\$left,\$top,\$right,\$bottom,\$color)

\$founded  = 0

_GetScreen_Initialize(\$right, \$bottom)
_GetScreen_GetScreen()

For \$x = \$left To \$right
For \$y = \$top To \$bottom
If _GetScreen_GetPixel(\$x, \$y) = \$color Then  \$founded += 1
Next
Next
Return \$founded
EndFunc```

Not much of a signature but working on it...

Spoiler

Share on other sites

I have one more question:

i Creat a while and check my red point.

I am shown 566 pixels. If I now move a window in front of the point, there are considerably more pixels.

How can that be?

```#include "GetScreen.au3"

While 1

ToolTip (_PixelCountInArea(1131, 460,1406, 750,"D05E5E"),600,500)
Sleep (100)

WEnd

Func _PixelCountInArea(\$left,\$top,\$right,\$bottom,\$color)

\$founded  = 0

_GetScreen_Initialize(\$right, \$bottom)
_GetScreen_GetScreen()

For \$x = \$left To \$right
For \$y = \$top To \$bottom
If _GetScreen_GetPixel(\$x, \$y) = \$color Then
\$founded += 1
EndIf
Next
Next
Return \$founded
EndFunc```

Share on other sites

Here's a fast (and small) C DLL version (attached, or compile with TCC)

Takes a file, change it to screencap yourself if needed.

```#include <GDIPlus.au3>

Global Static \$CountColorDll = DllOpen("CountColor.dll")
Global \$hImage, \$iLeft = 0, \$iTop = 0, \$iRight = 1100, \$iBottom = 600, \$hPixels, \$Count = 0, \$Color = 0xFFd05e5e

_GDIPlus_Startup()
\$hImage = _GDIPlus_BitmapCreateFromFile("image.png")

\$Count = _CountColor(\$hImage, \$Color)

_GDIPlus_Shutdown()
ConsoleWrite("Amount of " & Hex(\$Color, 8) & " = " & \$Count & @crlf)

Func _CountColor(ByRef \$hImage, \$Color)
\$Pixels = _GDIPlus_BitmapLockBits(\$hImage, \$iLeft, \$iTop, \$iRight, \$iBottom, \$GDIP_ILMREAD, \$GDIP_PXF32ARGB)
\$aRet = DllCall(\$CountColorDLL, "int:cdecl", "CountColor", "ptr", \$Pixels.Scan0, "int", (\$iRight-\$iLeft)*(\$iBottom-\$iTop), "int", \$Color)
_GDIPlus_BitmapUnlockBits(\$hImage, \$Pixels)
_GDIPlus_BitmapDispose(\$hImage)
Return \$aRet[0]
EndFunc

#cs
===================================================================================================
C code for the DLL, compile with 'Tiny C Compiler' (TCC) https://bellard.org/tcc/

tcc -shared CountColor.c
===================================================================================================
#include <stdlib.h>

__declspec(dllexport) int CountColor(int* pixel, int len, int color) {
int i, count = 0;
for(i=0;i<len;i++){if(pixel[i]==color){count+=1;}}
return count;}
===================================================================================================
#ce```

Btw, there are WAY more than 566 pixels that have color d05e5e, I count 28661 of them (and the edge of the circle is antialiased so they dont count)

Edited by Werty

Some guy's script + some other guy's script = my script!

Share on other sites
1 hour ago, Werty said:

Btw, there are WAY more than 566 pixels that have color d05e5e, I count 28661 of them

True !

For width and height I have given the complete screen (for trial purposes). I still use _GetScreen_GetPixel.

Screenshot without overlaying the area (the red dot) opened with Irfanview ==> 22291 Pixels found

Screenshot with overlaying the area (the red dot) opened with Irfanview ==> 6706 Pixels found

```#include <Timers.au3>
#include <MsgBoxConstants.au3>
#include <Constants.au3>
#include "GetScreen.au3"
#include <Array.au3>
Opt("MustDeclareVars", True)

HotKeySet("{ESC}", "_Terminate")
HotKeySet("+!1", "_PixelCount1") ; Shift-Alt-1 ==> start search with _GetScreen_GetPixel

Global \$iWidth, \$iHeight, \$sColor, \$iFound
; set values :
\$iWidth     = @DesktopWidth  ; Width of the primary display in pixels
\$iHeight    = @DesktopHeight ; Height of the primary display in pixels
\$sColor     = "D05E5E" ; color you are looking for
\$iFound     = 0 ; Pixels found

While True
Sleep(100)
WEnd

Func _PixelCount1()
Local \$iMsgID
Local \$hStarttime = _Timer_Init()
\$iFound  = 0

_GetScreen_Initialize(\$iWidth, \$iHeight)
_GetScreen_GetScreen()
For \$x = 0 To \$iWidth
For \$y = 0 To \$iHeight
If (Hex(_GetScreen_GetPixel(\$x, \$y), 6)) = \$sColor Then
\$iFound   += 1
EndIf
Next
Next

\$iMsgID = MsgBox(BitOR(\$MB_SYSTEMMODAL, \$MB_ICONINFORMATION, \$MB_RETRYCANCEL), _
"PixelSearch Result :", _
"Pixels checked : " & @CRLF & (\$iWidth * \$iHeight) & @CRLF & _
"Pixels found   : " & @CRLF & \$iFound   & @CRLF & @CRLF & _
"Time           : " & @CRLF & _Timer_Diff(\$hStarttime) & " ms" & @CRLF)
If \$iMsgID = \$IDCANCEL Then Exit
EndFunc

Func _Terminate()
MsgBox(BitOR(\$MB_SYSTEMMODAL, \$MB_ICONINFORMATION), "Message :", "Script terminated" & @CRLF)
Exit
EndFunc```

"In the beginning the Universe was created. This has made a lot of people very angry and been widely regarded as a bad move."

Share on other sites

The red dot is only a test. I dont want to scan image files.

So it's not possible to scan an area in a repeat loop and count the pixels?
And if the number of employees changes, the new number of employees should be output live.

@Nine Can't you do a magic trick?

```#include "GetScreen.au3"

While 1

ToolTip (_PixelCountInArea(1131, 460,1406, 750,"D05E5E"),600,500)
Sleep (100)

WEnd

Func _PixelCountInArea(\$left,\$top,\$right,\$bottom,\$color)

\$founded  = 0

_GetScreen_Initialize(\$right, \$bottom)
_GetScreen_GetScreen()

For \$x = \$left To \$right
For \$y = \$top To \$bottom
If _GetScreen_GetPixel(\$x, \$y) = \$color Then
\$founded += 1
EndIf
Next
Next
Return \$founded
EndFunc```

Edited by Dexter1993
Share on other sites
20 minutes ago, Dexter1993 said:

So it's not possible to scan an area in a repeat loop and count the pixels?

Of course it is possible.  Btw do not initialize repeatedly if the size of the screen does not change. Just do it once at the beginning of the script and only read the screen when you want to count the pixels.

```#include "..\GetScreen\GetScreen.au3"

Local \$hWnd = WinActivate("[CLASS:YourWindowClassHere]")
If Not \$hWnd Then Exit MsgBox(\$MB_SYSTEMMODAL, "Error", "Unable to find window")
_GetScreen_Initialize(@DesktopWidth, @DesktopHeight)

While True
ToolTip(_PixelCountInArea(700, 460, 800, 750, "D05E5E"), 600, 500)
Sleep(100)
WEnd

Func _PixelCountInArea(\$left, \$top, \$right, \$bottom, \$color)
Local \$founded = 0
_GetScreen_GetScreen()

For \$x = \$left To \$right
For \$y = \$top To \$bottom
If Hex(_GetScreen_GetPixel(\$x, \$y), 6) = \$color Then \$founded += 1
Next
Next
Return \$founded
EndFunc   ;==>_PixelCountInArea```

Edited by Nine

Not much of a signature but working on it...

Spoiler

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account