Jump to content

CPU gets high when using GDI


 Share

Recommended Posts

I'm working on a personal project that involves box counting algorithm and one module, that involves image processing, loads the CPU.

I have tried to put a delay between searches, but this decreases the speed in filling the boxes (see the ;Sleep(5)<<<<<<<<<<<<<<<<< in the script), although it decreases the CPU load drastically.

Should I stay with this solution or rewrite the whole module (GDI is something (still) new to me) ?

#AutoIt3Wrapper_AU3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6
#AutoIt3Wrapper_Run_Tidy=y
#Tidy_Parameters=/tc 0 /sfc

#include <WindowsConstants.au3>
#include <ScreenCapture.au3>
#include <GDIP.au3>;Thanks Authenticity!

;==================================================================================
; Application...:  Box Counting
; Module........:  Image processing
; Last Rev......:  August 01, 2011
; Author........:  M. Iancu (taietel at yahoo dot com)
;==================================================================================
HotKeySet("{ESC}", "_Exit")

Global $sBuffer = ""
InetGet("http://sirop-artar.info/images/frunza-artar.jpg", @ScriptDir & "\frunza.jpg")
Global $sImTest = @ScriptDir & "\frunza.jpg";test image
Global $sImgOutDir = @ScriptDir & "\Images";here we store the snapshots at the end of each step

_Box_CountModule($sImTest)

Func _Box_CountModule($sIm)
 If Not FileExists($sImgOutDir) Then DirCreate($sImgOutDir)
 Local $bFinish = 0
 Local $iTimerStart = TimerInit();for testing
 ;create image container
 Local $hGUI = GUICreate("BoxCounting", 500, 500, -1, -1, BitOR($WS_POPUP, $WS_BORDER), $WS_EX_TOPMOST)
 GUISetState()
 ;for testing
 __Test("RetVal" & @TAB & "Step" & @TAB & "ImgWidth" & @TAB & "ImgHeight" & @TAB & "Grid(Boxes)" & @TAB & "BoxWidth" & @TAB & "FilledBoxes" & @TAB & "FilledArea")
 For $i = 0 To 15
  $bFinish = _Box_LoadImage($hGUI, $sIm, $i)
  ;for testing
  __Test($bFinish[0] & @TAB & $bFinish[1] & @TAB & $bFinish[2] & @TAB & @TAB & $bFinish[3] & @TAB & @TAB & $bFinish[4] & @TAB & @TAB & $bFinish[5] & @TAB & @TAB & $bFinish[6] & @TAB & @TAB & $bFinish[7])
  ;if the limit of 1px is reached, exit loop
  If $bFinish[0] Then ExitLoop
  Sleep(400);some pause between steps, to see the transitions
 Next
 Local $iTimerEnd = TimerDiff($iTimerStart)
 __Test("Finished in " & Round($iTimerEnd / 1000, 2) & " sec.");for testing
 Local $sFile = @ScriptDir & "\test.txt";for testing
 Local $hFile = FileOpen($sFile, 18)
 FileWrite($hFile, $sBuffer)
 FileClose($hFile)
 ShellExecute($sFile)
 Exit

 While 1
  Sleep(10)
  Switch GUIGetMsg()
   Case -3
    Exit
  EndSwitch
 WEnd
EndFunc   ;==>_Box_CountModule

Func _Box_LoadImage($hWnd, $sImage, $iB = 0)
 Local $aResults[8]
 $aResults[1] = $iB + 1;No. of iterations
 Local $iFilledBoxes = 0
 Local $bFilled = False

 Local $iDimension, $iHeight, $iWidth, $hImage, $hBitmap, $hBrush, $hGraphic, $lColour
 ;Start GDI+
 _GDIPlus_Startup()
 ;Load image
 $hImage = _GDIPlus_ImageLoadFromFile($sImage)

 ; If the image is not squared, it will be by filling the width/height
 ; (depends which is longer) with the colour of first pixel
 $iWidth = _GDIPlus_ImageGetWidth($hImage)
 $iHeight = _GDIPlus_ImageGetHeight($hImage)
 $aResults[2] = $iWidth
 $aResults[3] = $iHeight


 Local $hClone = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $iWidth, $iHeight, $GDIP_PXF01INDEXED);1px indexed
 $lColour = _GDIPlus_BitmapGetPixel($hClone, 0, 0)
 _GDIPlus_ImageDispose($hImage)

 If $iWidth > $iHeight Then
  $iDimension = $iWidth
 Else
  $iDimension = $iHeight
 EndIf

 Local $iBoxes = 2 ^ $iB
 Local $iBoxWidth = Ceiling($iDimension / $iBoxes)
 $aResults[4] = $iBoxes ^ 2
 $aResults[5] = $iBoxWidth
 ;reinitialize dimension
 $iDimension = $iBoxWidth * $iBoxes

 ;and create a new squared image with dimension x dimension, 1px indexed
 $hBitmap = _GDIPlus_BitmapCreateFromScan0($iDimension + 1, $iDimension + 1, 0, $GDIP_PXF01INDEXED)
 $hGraphic = _GDIPlus_ImageGetGraphicsContext($hBitmap)
 _GDIPlus_GraphicsClear($hGraphic, $lColour)
 _GDIPlus_GraphicsDrawImageRect($hGraphic, $hClone, 0, 0, $iWidth, $iHeight)

 _GDIPlus_GraphicsDispose($hGraphic)
 _GDIPlus_ImageDispose($hClone)
 ;check if 1px limit is reached; in this case, return True
 If $iBoxWidth <= 1 Then
  $aResults[0] = True
 Else
  $aResults[0] = False
 EndIf

 ;redim the GUI with at width/height of the image
 WinMove($hWnd, "", (@DesktopWidth - $iWidth) / 2, (@DesktopHeight - $iHeight) / 2, $iWidth, $iHeight)

 $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hWnd)
 _GDIPlus_GraphicsClear($hGraphic, $lColour)
 _GDIPlus_GraphicsSetSmoothingMode($hGraphic, 2)
 ;draw the newly created image onto this graphic
 _GDIPlus_GraphicsDrawImage($hGraphic, $hBitmap, 0, 0)

 ;set the quality of the captured images
 _ScreenCapture_SetJPGQuality(80)
 ; capture the first one for later use
 If $iB = 0 Then _ScreenCapture_CaptureWnd($sImgOutDir & "\img0.jpg", $hWnd, 0, 0, -1, -1, False)

 ;create a brush for filling the boxes
 $hBrush = _GDIPlus_BrushCreateSolid()
 _GDIPlus_BrushSetSolidColor($hBrush, 0x99FF0000)
 ;start searching for pixels different from the first
 For $i = 0 To $iDimension - $iBoxWidth Step $iBoxWidth
  For $j = 0 To $iDimension - $iBoxWidth Step $iBoxWidth
   If $i > $iHeight Or $j > $iWidth Then ExitLoop ; if width/height is reached, exit loop (now the image is squared and don't know which width/height was shorter/longer)
   For $m = 0 To $iBoxWidth
    For $n = 0 To $iBoxWidth
     ; if scanned colour is different from the background, exit loop
     If $lColour <> _GDIPlus_BitmapGetPixel($hBitmap, $j + $n, $i + $m) Then
      $bFilled = True
      ExitLoop
     EndIf
    Next
    If $bFilled Then ExitLoop
   Next
   If $bFilled Then
    _GDIPlus_GraphicsFillRect($hGraphic, $j, $i, $iBoxWidth, $iBoxWidth, $hBrush)
    $iFilledBoxes += 1
    $bFilled = False
   EndIf
   ;Sleep(5); <<<<<<<<<<<<<<<<<<<<<<<<<<<< to reduce CPU usage. Any other way??? :/
  Next
 Next
 ; capture the filled images for later use
 _ScreenCapture_CaptureWnd($sImgOutDir & "\img" & $iB + 1 & ".jpg", $hWnd, 0, 0, -1, -1, False)
 $aResults[6] = $iFilledBoxes
 $aResults[7] = $iFilledBoxes * ($iBoxWidth ^ 2);filled surface (px^2)
 ;clean the rest of the resources
 _GDIPlus_BrushDispose($hBrush)
 _GDIPlus_BitmapDispose($hBitmap)
 _GDIPlus_GraphicsDispose($hGraphic)
 _GDIPlus_Shutdown()

 Return $aResults
EndFunc   ;==>_Box_LoadImage

Func _Exit()
 Exit
EndFunc   ;==>_Exit
#region Internal use
Func __Test($sT)
 ConsoleWrite($sT & @CRLF)
 $sBuffer &= $sT & @CRLF
EndFunc   ;==>__Test
#endregion Internal use

Thanks in advance for answering to this.

Link to comment
Share on other sites

One small refinement:

You might pull the GDI Startup and Shutdown statements from the BoxLoadImage() function and place them instead on both sides of the for/next loop in BoxCountModule(). I'm assuming 15 less calls to GDIStartup will make your CPU vs. speed issue slightly easier to balance.

Maybe there is more initialization/termination code that could be moved and executed only once instead of 16 times?

Edited by Spiff59
Link to comment
Share on other sites

Spiff59, thank you for the suggestion. :)

The maximum number of calls, as tested so far, was 11 and the cycle stops when 1px limit is reached. I put 15 in case the image is very large.

Link to comment
Share on other sites

You are calling too much GDI+ functions in the loop which can be move outside the loops.

This is just an example not creating Minkowski–Bouligand dimension (box counting algorithm):

#include <ScreenCapture.au3>

If Not FileExists(@ScriptDir & "\frunza.jpg") Then
    InetGet("http://sirop-artar.info/images/frunza-artar.jpg", @ScriptDir & "\frunza.jpg")
    If @error Then Exit
EndIf

_GDIPlus_Startup()
$hBitmap = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\frunza.jpg")
$iW = _GDIPlus_ImageGetWidth($hBitmap)
$iH = _GDIPlus_ImageGetHeight($hBitmap)
$hBMP_2bpp = _GDIPlus_BitmapCloneArea($hBitmap, 0 ,0 , $iW, $iH, $GDIP_PXF01INDEXED)
$hBrush = _GDIPlus_BrushCreateSolid(0xA0FF0000)

$hGUI = GUICreate("Test", $iW, $iH)
GUISetState()

$hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)

HotKeySet("{ESC}", "_Exit")
Global $i = 3, $j = 0

Do
    _GDIPlus_GraphicsDrawImage($hGraphic, $hBMP_2bpp, 0, 0)
    Get_BoxCountingDimension(Floor($i), $hGraphic, $iW, $iH, $hBMP_2bpp)
    _ScreenCapture_CaptureWnd(@ScriptDir & "\Image" & StringFormat("%02i", $j) & ".jpg", $hGUI, 0, 0, -1, -1, False)
    $j += 1
    Sleep(2000)
    $i *= 1.5
Until $i > 300

MsgBox(0, "Test", "Done", 10)
While 1
    Switch GUIGetMsg()
        Case -3
            _Exit()
    EndSwitch
WEnd

Func Get_BoxCountingDimension($depth, $hContext, $iW, $iH, $hBitmap)
    Local $x, $y, $draw, $w = Ceiling($iW / $depth), $h = Ceiling($iH / $depth)
    For $y = 0 To $iH Step $h
        For $x = 0 To $iW Step $w
            $draw = CheckPixels($hBMP_2bpp, $x, $y, $w, $h)
            If $draw Then _GDIPlus_GraphicsFillRect($hGraphic, $x, $y, $w, $h, $hBrush)
        Next
    Next
    Return
EndFunc

Func CheckPixels($hBitmap, $x, $y, $w, $h, $color = "0xFF000000")
    Local $px, $py, $c
    For $py = $y To $y + $h
        For $px = $x To $x + $w
            $c = DllCall($ghGDIPDll, "uint", "GdipBitmapGetPixel", "hwnd", $hBitmap, "int", $px, "int", $py, "uint*", 0)
            If $c[4] = $color Then Return True
        Next
    Next
    Return False
EndFunc

Func _Exit()
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_BitmapDispose($hBMP_2bpp)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_ShutDown()
    GUIDelete($hGUI)
    Exit
EndFunc

Btw, interesting stuff you are doing here.

Br,

UEZ

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

UEZ, I knew you will come up sooner or later. :)

Your script is more than I have expected. Thank you!

The main application is based on Minkowski–Bouligand, but I want to extend it to determine area of irregular shapes (like some graphs, maps etc) also, based on a given scale factor. It can be used for a lot of other interesting things. The resulting report will be in pdf format, including the images taken from every step of the process, graphs etc.

Thanks again!

taietel

Edited by taietel
Link to comment
Share on other sites

I made some changes that you can follow now how the rect. will get smaller and it will save the image, too.

Br,

UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to comment
Share on other sites

I made some modifications to fit my needs: the box has to be squared, capture just the caret etc.

Simple, but briliant, the idea of working on the black&white picture and showing the real picture to the user.

#include <ScreenCapture.au3>
#include <WindowsConstants.au3>

Opt("CaretCoordMode",0)

If Not FileExists(@ScriptDir & "\frunza.jpg") Then
    InetGet("http://sirop-artar.info/images/frunza-artar.jpg", @ScriptDir & "\frunza.jpg")
    If @error Then Exit
EndIf

_GDIPlus_Startup()

$hBitmap = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\frunza.jpg")
$iW = _GDIPlus_ImageGetWidth($hBitmap)
$iH = _GDIPlus_ImageGetHeight($hBitmap)
If $iW>=$iH Then
 $iD=$iW
Else
 $iD=$iH
EndIf
$hBMP_2bpp = _GDIPlus_BitmapCloneArea($hBitmap, 0 ,0 , $iW, $iH, $GDIP_PXF01INDEXED)
$hBrush = _GDIPlus_BrushCreateSolid(0xA000FF00)

$hGUI = GUICreate("Test", $iW, $iH)
GUISetState()
$iC = WinGetCaretPos()

$hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI)
_GDIPlus_GraphicsSetSmoothingMode($hGraphic,2)

HotKeySet("{ESC}", "_Exit")
Global $i = 0, $j = 0, $iBoxes, $iBoxWdth, $iFilled
_ScreenCapture_SetJPGQuality(90)
__Test("Step" & @TAB & @TAB & "Box Width" & @TAB & "Boxes" & @TAB & @TAB & "Filled");test

Do
 _GDIPlus_GraphicsDrawImageRect($hGraphic, $hBitmap, 0, 0,$iW, $iH)
 $iBoxes=2^$i
 $iBoxWdth=$iD/$iBoxes;square box
 $iFilled=0
 If $j=0 Then
  Sleep(300)
  _ScreenCapture_CaptureWnd(@ScriptDir & "\Image00.jpg", $hGUI, $iC[0], $iC[1], $iW, $iH, False)
  $iFilled=1
 Else
  Get_BoxCountingDimension($iBoxes, $hGraphic, $iW, $iH, $hBMP_2bpp)
  _ScreenCapture_CaptureWnd(@ScriptDir & "\Image" & StringFormat("%02i", $j) & ".jpg", $hGUI, $iC[0], $iC[1], $iW, $iH, False)
 EndIf
 __Test($j & @TAB & @TAB & StringFormat("%.2f",$iBoxWdth) & @TAB & @TAB & $iBoxes^2 & @TAB & @TAB & $iFilled);test
 $j += 1
    $i += 1
 Sleep(500)
Until $iBoxWdth < 1

While 1
 Sleep(10)
    Switch GUIGetMsg()
        Case -3
            _Exit()
    EndSwitch
WEnd

Func Get_BoxCountingDimension($depth, $hContext, $iW, $iH, $hBitmap)
    Local $x, $y, $draw, $w = Ceiling($iD / $depth), $h = Ceiling($iD / $depth)
    For $y = 0 To $iH Step $h
        For $x = 0 To $iW Step $w
            $draw = CheckPixels($hBMP_2bpp, $x, $y, $w, $h)
            If $draw Then
    $iFilled += 1
    _GDIPlus_GraphicsFillRect($hGraphic, $x, $y, $w, $h, $hBrush)
   EndIf
        Next
    Next
    Return
EndFunc

Func CheckPixels($hBitmap, $x, $y, $w, $h, $color = "0xFF000000")
    Local $px, $py, $c
    For $py = $y To $y + $h
        For $px = $x To $x + $w
            $c = DllCall($ghGDIPDll, "uint", "GdipBitmapGetPixel", "hwnd", $hBitmap, "int", $px, "int", $py, "uint*", 0)
            If $c[4] = $color Then Return True
        Next
    Next
    Return False
EndFunc

Func _Exit()
    _GDIPlus_BrushDispose($hBrush)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_BitmapDispose($hBMP_2bpp)
    _GDIPlus_GraphicsDispose($hGraphic)
    _GDIPlus_ShutDown()
    GUIDelete($hGUI)
    Exit
EndFunc

Func __Test($sT)
 ConsoleWrite($sT & @CRLF)
EndFunc   ;==>__Test

Thanks (again and again and again ...) UEZ!:)

taietel

Link to comment
Share on other sites

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

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...