Jump to content
Sign in to follow this  
Jardz

GDI Plus Image Histogram

Recommended Posts

Jardz

Hi all,

I'm trying to better understand the excellent GDIP UDF by Authenticity

So I set myself a simple project to try and create a tonal histogram of an image; I use this in Photoshop a lot.

I started with an old example by Toady at the below link (Thank you Toady for getting me started)

Unfortunately 'PixelGetColor' seems very slow, so I wanted to see how fast GdipGetPixel would be.

So, starting with Toady's example, and using GdipGetPixel function by Vossen and ImageScale and Greyscale functions by Uez (Thank you to all) I have the script below...

#include <GDIP.au3>
#include <Array.au3>
#include <GUIConstants.au3>


Global $iSampleRate = 20000 ; Number of pixels read for histogram, the higher the more accurate but slower.
Global $histos, $histo

$gui = GUICreate("Histogram",278,200,-1,-1)
$pen = GuiCtrlCreateGraphic(10,10, 258, 150)
GUICtrlSetBkColor(-1,0xFFFFFF)

GUICtrlCreateLabel("Sample Rate:",20,160)
$sample = GUICtrlCreateInput($iSampleRate,20,175,70,20)

;GUICtrlSetState(-1, $GUI_DISABLE)
$BtnOpen= GUICtrlCreateButton("Open", 180, 165, 78, 30)
GUISetState()

While 1
Switch GUIGetMsg()
Case $BtnOpen
_main()

Case $GUI_EVENT_CLOSE
_GDIPlus_ShutDown()
Exit

EndSwitch
WEnd

Func _main()
Local $tmp
$iSampleRate = GUICtrlRead($sample)
If $tmp < 1 and $tmp > 99999999 Then
GUICtrlSetData($sample,10000)
$iSampleRate = 10000
EndIf
Local $sFile = FileOpenDialog("Open File", @WorkingDir, "All Picture Files (*.bmp;*.gif;*.jpg;*.jpeg;*.tif;*.tiff;*.png)", 3)
Local $t = TimerInit()
$aArrayHist = _Histogram($sFile, $iSampleRate)
Local $d = Round(TimerDiff($t)/1000,2)
ConsoleWrite("Time to build histogram = " & $d & @CRLF)
RenderHistogram($aArrayHist)
Local $d = Round(TimerDiff($t)/1000,2)
ConsoleWrite("Total Time = " & $d & @CRLF)
EndFunc

Func RenderHistogram($a)
Local $max = _ArrayMax($a,1,1)
GuiCtrlDelete($pen)
$pen=GuiCtrlCreateGraphic(10,10, 258, 150)
GUICtrlSetBkColor(-1,0xFFFFFF)
GUICtrlSetState(-1, $GUI_DISABLE)
For $i = 1 To UBound($a)-1
GUICtrlSetGraphic($pen,$GUI_GR_MOVE, $i,150)
GUICtrlSetGraphic($pen,$GUI_GR_LINE, $i,150 - Round(140*($a[$i]/$max),0))
Next
GUICtrlSetColor(-1,0x000000)
EndFunc

Func _Histogram($fImage, $iSampleRate)
If $fImage = "" Then Return SetError(1, 0, 0)
If Not FileExists($fImage) Then Return SetError(2, 0, 0)

Local $fName = StringRegExpReplace($fImage, ".*\\(.*).{4}", "$1")
Local $declared = True
If Not $ghGDIPDll Then
_GDIPlus_Startup()
$declared = False
EndIf
Local $hImageFromFile = _GDIPlus_ImageLoadFromFile($fImage)
Local $iWidth = _GDIPlus_ImageGetWidth($hImageFromFile)
Local $iHeight = _GDIPlus_ImageGetHeight($hImageFromFile)

Local $ratio
If $iWidth*$iHeight > $iSampleRate Then
$ratio = $iWidth/$iHeight
$iW = Int(Sqrt($iSampleRate*$ratio))
$iH = Int(sqrt($iSampleRate/$ratio))
Else
$iW = $iWidth
$iH = $iHeight
EndIf

Local $hImageThumbnail = DllCall($ghGDIPDll, "uint", "GdipCreateBitmapFromScan0", "int", $iW, "int", $iH, "int", 0, "int", 0x0026200A, "ptr", 0, "int*", 0)
If @error Then Return SetError(3, 0, 0)
$hImageThumbnail = $hImageThumbnail[6]
Local $hBmpCtxt = _GDIPlus_ImageGetGraphicsContext($hImageThumbnail)
DllCall($ghGDIPDll, "uint", "GdipSetInterpolationMode", "handle", $hBmpCtxt, "int", 7)
_GDIPlus_GraphicsDrawImageRect($hBmpCtxt, $hImageFromFile, 0, 0, $iW, $iH)
_GDIPlus_ImageDispose($hImageFromFile)
_GDIPlus_GraphicsDispose($hBmpCtxt)

; If you don't want to transfer to greyscale then comment out below
;#CS
$hImageContext = _GDIPlus_ImageGetGraphicsContext($hImageThumbnail)
Local $tNegMatrix, $pNegMatrix
$tNegMatrix = _GDIPlus_ColorMatrixCreateGrayScale()
$pNegMatrix = DllStructGetPtr($tNegMatrix)
$hIA = _GDIPlus_ImageAttributesCreate()
_GDIPlus_ImageAttributesSetColorMatrix($hIA, 0, True, $pNegMatrix)
_GDIPlus_GraphicsDrawImageRectRectIA($hImageContext, $hImageThumbnail, 0, 0, $iW, $iH, 0, 0, $iW, $iH, $hIA)
_GDIPlus_ImageAttributesDispose($hIA)
_GDIPlus_GraphicsDispose($hImageContext)
;#CE

$aHist = _BuildHistogramArray($hImageThumbnail)
_GDIPlus_ImageDispose($hImageThumbnail)
_GDIPlus_Shutdown()
Return $aHist
EndFunc

Func _BuildHistogramArray($hBitmap)

Local $tArgb, $pArgb, $aRet, $blue, $red, $green, $w, $h
$tArgb = DllStructCreate("dword Argb")
$pArgb = DllStructGetPtr($tArgb)

Local $Hist[257]
For $i = 0 To UBound($Hist) - 1
$Hist[$i] = 0
Next

$w = _GDIPlus_ImageGetWidth($hBitmap)
$h = _GDIPlus_ImageGetHeight($hBitmap)
ConsoleWrite("Thumbnail W = " & $w & @CRLF & "Thumbnail H = " & $h & @CRLF)

;-------------Use the following if you've changed the bitmap thumbnail to greyscale otherwise comment out and use the next one----------
For $i = 1 To $h
For $j = 1 To $w
$aRet = DllCall($ghGDIPDll, "int", "GdipBitmapGetPixel", "hwnd", $hBitmap, "int", $j, "int", $i, "ptr", $pArgb)
$Hist[Dec(Hex(DllStructGetData($tArgb, "Argb"),2))+1] +=1
$Hist[0] += 1
Next
Next

;-------------If you're keeping bitmap as RGB and not Greyscale then use below to build Array for Histogram------------------------------
#cs
For $i = 1 To $h
For $j = 1 To $w
$aRet = DllCall($ghGDIPDll, "int", "GdipBitmapGetPixel", "hwnd", $hBitmap, "int", $j, "int", $i, "ptr", $pArgb)
$color_hex = "0x" & Hex(DllStructGetData($tArgb, "Argb") ,6)
$Hist[Round((BitAND($color_hex,255) + BitShift(BitAND($color_hex,65280),0x08) + BitShift(BitAND($color_hex,16711680),0x10))/3,0)+1] += 1
$Hist[0] += 1
Next
Next
#ce
$Hist[2] = ($Hist[1] + $Hist[3])/2
ConsoleWrite("Total Count = " & $Hist[0] & @CRLF)
Return $Hist

EndFunc

Func ScaleImage($hImage, $iScaleW, $iScaleH, $iInterpolationMode = 7) ;coded by UEZ 2012
Local $iWidth = _GDIPlus_ImageGetWidth($hImage) * $iScaleW
Local $iHeight = _GDIPlus_ImageGetHeight($hImage) * $iScaleH
Local $hBitmap = DllCall($ghGDIPDll, "uint", "GdipCreateBitmapFromScan0", "int", $iWidth, "int", $iHeight, "int", 0, "int", 0x0026200A, "ptr", 0, "int*", 0)
If @error Then Return SetError(3, 0, 0)
$hBitmap = $hBitmap[6]
Local $hBmpCtxt = _GDIPlus_ImageGetGraphicsContext($hBitmap)
DllCall($ghGDIPDll, "uint", "GdipSetInterpolationMode", "handle", $hBmpCtxt, "int", $iInterpolationMode)
_GDIPlus_GraphicsDrawImageRect($hBmpCtxt, $hImage, 0, 0, $iWidth, $iHeight)
_GDIPlus_ImageDispose($hImage)
_GDIPlus_GraphicsDispose($hBmpCtxt)
Return $hBitmap
EndFunc

However, the reason I'm posting in General Help and Support is I found two functions within GDIP v1.1 (Vista and above?) that may be exactly what I want.

two dllcalls instead of 2000

GdipBitmapGetHistogramSize and GdipBitmapGetHistogram

http://msdn.microsoft.com/en-us/library/windows/desktop/ms536307(v=vs.85).aspx

I know nothing about dllcalls but I've tried altering various examples in GDIP.au3 to see if I can get these to work.

After hours of trying on the 'GdipBitmapGetHistogramSize' I've realized I have no hope without a better understanding; I keep getting '0' as a result.

I would greatly appreciate anyone who comes across this post with the knowledge and time to show me how to call these functions.

Many thanks

Jardz

Share this post


Link to post
Share on other sites
UEZ

Do you mean something like that here?

#include <GDIPlus.au3>

Global Enum $HistogramFormatARGB = 0, $HistogramFormatPARGB, $HistogramFormatRGB, $HistogramFormatGray, _
                            $HistogramFormatB, $HistogramFormatG, $HistogramFormatR, $HistogramFormatA

;http://msdn.microsoft.com/en-us/library/windows/desktop/ms534136(v=vs.85).aspx
Global Const $ImageFlagsNone = 0
Global Const $ImageFlagsScalable = 0x0001
Global Const $ImageFlagsHasAlpha = 0x0002
Global Const $ImageFlagsHasTranslucent  = 0x0004
Global Const $ImageFlagsPartiallyScalable = 0x0008
Global Const $ImageFlagsColorSpaceRGB = 0x0010
Global Const $ImageFlagsColorSpaceCMYK = 0x0020
Global Const $ImageFlagsColorSpaceGRAY = 0x0040
Global Const $ImageFlagsColorSpaceYCBCR = 0x0080
Global Const $ImageFlagsColorSpaceYCCK = 0x0100
Global Const $ImageFlagsHasRealDPI = 0x1000
Global Const $ImageFlagsHasRealPixelSize = 0x2000
Global Const $ImageFlagsReadOnly = 0x00010000
Global Const $ImageFlagsCaching = 0x00020000

Global $file = FileOpenDialog("Select Image", "", "Images (*.jpg;*.png;*.bmp;*.gif)")
If @error Then Exit
_GDIPlus_Startup()
Global $hBitmap = _GDIPlus_BitmapCreateFromFile($file)

Global $iHistogramFormat = $HistogramFormatRGB, $bAlpha = False

Global $aRes = DllCall($ghGDIPDll, "int", "GdipGetImageFlags", "handle", $hBitmap, "uint*", 0)
If BitAND($aRes[2], $ImageFlagsHasAlpha) Then
        $iHistogramFormat = $HistogramFormatARGB
        $bAlpha = True
EndIf
Global Const $iGetHistogramSize = _GDIPlus_BitmapGetHistogramSize($iHistogramFormat)

Global $tStructChannel0 = DllStructCreate("uint channel0[" & $iGetHistogramSize & "]")
Global $pStructChannel0 = DllStructGetPtr($tStructChannel0)
Global $tStructChannel1 = DllStructCreate("uint channel1[" & $iGetHistogramSize & "]")
Global $pStructChannel1 = DllStructGetPtr($tStructChannel1)
Global $tStructChannel2 = DllStructCreate("uint channel2[" & $iGetHistogramSize & "]")
Global $pStructChannel2 = DllStructGetPtr($tStructChannel2)
Global $tStructChannel3 = DllStructCreate("uint channel3[" & $iGetHistogramSize & "]")
Global $pStructChannel3 = 0
If $bAlpha Then $pStructChannel3 = DllStructGetPtr($tStructChannel3)


$r = _GDIPlus_BitmapGetHistogram($hBitmap, $iHistogramFormat, $iGetHistogramSize, $pStructChannel0, $pStructChannel1, $pStructChannel2, $pStructChannel3)


Global $a = 0, $r, $g, $b

For $i = 1 To $iGetHistogramSize
    If $bAlpha Then
        $a = DllStructGetData($tStructChannel0, "channel0", $i)
        $r = DllStructGetData($tStructChannel1, "channel1", $i)
        $g = DllStructGetData($tStructChannel2, "channel2", $i)
        $b = DllStructGetData($tStructChannel3, "channel3", $i)
    Else
        $r = DllStructGetData($tStructChannel0, "channel0", $i)
        $g = DllStructGetData($tStructChannel1, "channel1", $i)
        $b = DllStructGetData($tStructChannel2, "channel2", $i)
    EndIf
    ConsoleWrite("Alpha: " & @TAB & $a & @TAB & "R: " & $r & @TAB & @TAB & "G: " & $g & @TAB & @TAB & "B: " & $B & @LF)
Next


_GDIPlus_BitmapDispose($hBitmap)
_GDIPlus_Shutdown()

Exit
;http://msdn.microsoft.com/en-us/library/windows/desktop/ms536307(v=vs.85).aspx
Func _GDIPlus_BitmapGetHistogram($hBitmap, $iFormat, $iGetHistogramSize, $pStructChannel0, $pStructChannel1, $pStructChannel2, $pStructChannel3)
    Local $aRes =  DllCall($ghGDIPDll, "bool", "GdipBitmapGetHistogram", "handle", $hBitmap, "uint", $iFormat, "uint",  $iGetHistogramSize, "struct*", $pStructChannel0,  "struct*", $pStructChannel1,  "struct*", $pStructChannel2, "struct*", $pStructChannel3)
    If @error Then Return SetError(1, @error, 0)
    Return 1
EndFunc

;http://msdn.microsoft.com/en-us/library/windows/desktop/ms536308(v=vs.85).aspx
Func _GDIPlus_BitmapGetHistogramSize($iFormat)
    Local $aRes = DllCall($ghGDIPDll, "int", "GdipBitmapGetHistogramSize", "uint", $iFormat, "dword*", 0)
    If @error Then Return SetError(1, @error, 0)
    Return $aRes[2]
EndFunc

For an example have a look here: http://www.autoitscript.com/forum/index.php?showtopic=147777

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!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
Jardz

That's Genius! Thank you very much UEZ.

Looking at how you've done this I realise my own efforts were nowhere close and never going to work :P .

I have a lot to learn...

Again, thank you!

Share this post


Link to post
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
Sign in to follow this  

×