Jump to content
dejhost

Extracting color-values of image along a user specified line

Recommended Posts

Happy New Year everyone!

I would like to extract the color values of certain pixels in an image.  This is how I picture the workflow: 

  1. User opens an image of his choice. Image is shown on the screen.
  2. User draws a line into the image. This happens by marking the startpixel and the endpixel of the line
  3. The line is drawn, so the user can check visually if he is happy with the line.
  4.  The following pixel based properties belonging to the line are stored in Excel:
    1. X-Coordinate
    2. Y-Coordinate
    3. Color Value
  5. Additional operations: Extracting for max- and min. color values; Statistical operations.  

Browsing through the helpfile of AutoIt I find plenty of functions for treating images (e.g. GDIPlus), but I am completely unsure if Autoit will get me there. Should I read the entire image into an array? Should I rather attempt to script an external image software (e.g. IrfanView, Gimp)?

I would be very thankful if someone could give some recommendations and maybe list a couple of the most important commands to use.

Thank you very much.

Dejhost

 

 

Share this post


Link to post
Share on other sites
Posted (edited)

Hello again,

documenting roughly what I've done so far:

  • Open image: 
_GDIPlus_ImageLoadFromFile 

But how to retrieve the pixel-coordinates that the line covers? Is there a way to get a 2D-array?

Afterward, I would use the following function to get the pixel colors:

_GDIPlus_BitmapGetPixel

Thanks in advance for any advice!

Edited by dejhost
grammar

Share this post


Link to post
Share on other sites
Posted (edited)

Yes it is totally doable with GDI+.  There are a few examples in the help file that are somewhat close to what you want to create.  The functions that you mentioned will certainly be part of the solution...To get the pixel, you will probably have to use some of the basic geometry that you learned in HS.

Happy scripting !

Edited by Nine

Share this post


Link to post
Share on other sites

Thank you for your encouraging words. So at least I am on the right track :-)

If I have to rely on my HS-memories, the result would not be accurate: I recall that there are plenty of ways to connect 2 points based on pixels. I'd rather use the same method for retrieving the pixel coordinates as the AutoIt uses for drawing the line:

Within the GDIPlus.au3, I find the function

_GDIPlus_GraphicsDrawLine

calls for a dll:

Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipDrawImageRectRect", "handle", $hGraphics, "handle", $hImage, _
            "float", $nDstX, "float", $nDstY, "float", $nDstWidth, "float", $nDstHeight, _
            "float", $nSrcX, "float", $nSrcY, "float", $nSrcWidth, "float", $nSrcHeight, _
            "int", $iUnit, "handle", $pAttributes, "ptr", 0, "ptr", 0)

I will try to track down this dll. Hoping it is so kind to tell me how it draws lines :-)

Share this post


Link to post
Share on other sites

nn for that complex dllcalll.  dont over-complex your script when you can do it with std func.

Share this post


Link to post
Share on other sites
Posted (edited)
10 hours ago, Nine said:

nn for that complex dllcalll.  dont over-complex your script when you can do it with std func.

What do you mean by "std func"? Standard functions?

If you know a function that could suit, please share. thx.

Edited by dejhost

Share this post


Link to post
Share on other sites
3 hours ago, dejhost said:

What do you mean by "std func"? Standard functions?

Yes here to start you up :

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <WinAPIHObj.au3>
#include <Color.au3>

Global $g_hGUI, $g_hGfxCtxt, $g_hBitmap, $g_hBMP, $g_hGraphics
Global $aCursA, $aCursB, $Completed = False

    AutoItSetOption("GUIOnEventMode", 1)

    _GDIPlus_Startup()

    $g_hBMP = _GDIPlus_BitmapCreateFromFile ("BaseImage.jpg")
    Global $iWidth = _GDIPlus_ImageGetWidth ($g_hBMP), $iHeight = _GDIPlus_ImageGetHeight ($g_hBMP)

    $g_hGUI = GUICreate("GDI+", $iWidth, $iHeight+100)
    $idButton = GUICtrlCreateButton ("Completed", int($iWidth/2)-100, $iHeight+30, 200, 50)
    GUICtrlSetOnEvent(-1, "Complete")

    GUISetState(@SW_SHOW)

    $g_hGraphics = _GDIPlus_GraphicsCreateFromHWND($g_hGUI) ;create a graphics object from a window handle
    $g_hBitmap = _GDIPlus_BitmapCreateFromGraphics($iWidth, $iHeight, $g_hGraphics)
    $g_hGfxCtxt = _GDIPlus_ImageGetGraphicsContext($g_hBitmap)

    GUISetOnEvent($GUI_EVENT_CLOSE, "_Exit")

    _GDIPlus_GraphicsDrawImage($g_hGfxCtxt, $g_hBMP, 0, 0) ;draw bitmap to backbuffer

    Do
        _GDIPlus_GraphicsDrawImageRect($g_hGraphics, $g_hBitmap, 0, 0, $iWidth, $iHeight) ;copy drawn bitmap to graphics handle (GUI)
        $aCursA = GUIGetCursorInfo ($g_hGUI)
        if @error then ContinueLoop
        $aCursB = $aCursA
        Local $iX = -1, $iY = -1

        While $aCursB[2] and $aCursB[0] <= $iWidth and $aCursB[1] <= $iHeight
          $aCursB = GUIGetCursorInfo ($g_hGUI)
          if $iX = $aCursB[0] and $iY = $aCursB[1] then ContinueLoop
          $iX = $aCursB[0]
          $iY = $aCursB[1]
          _GDIPlus_GraphicsClear($g_hGfxCtxt, 0xFF000000) ;clear bitmap for repaint
          _GDIPlus_GraphicsDrawImage($g_hGfxCtxt, $g_hBMP, 0, 0)
          _GDIPlus_GraphicsDrawImageRect($g_hGraphics, $g_hBitmap, 0, 0, $iWidth, $iHeight)
          _GDIPlus_GraphicsDrawLine($g_hGraphics, $aCursA[0], $aCursA[1], $aCursB[0], $aCursB[1])
          _GDIPlus_GraphicsDrawLine($g_hGfxCtxt, $aCursA[0], $aCursA[1], $aCursB[0], $aCursB[1])
        Wend

        Sleep (100)

    Until $Completed


    _Exit ()

Func Complete ()

  $Completed = True

EndFunc

Func _Exit()

  _GDIPlus_GraphicsDispose($g_hGfxCtxt)
  _GDIPlus_GraphicsDispose($g_hGraphics)
  _GDIPlus_BitmapDispose($g_hBitmap)
  _GDIPlus_BitmapDispose($g_hBMP)
  _GDIPlus_Shutdown()
  GUIDelete($g_hGUI)

EndFunc

 

Share this post


Link to post
Share on other sites

This _PixelsInLineToArray function will calculate the "x" or "y" value from the equation of a straight line of two points.

Example:- If the two points are pt1(10,10) and pt2(13, 30) then the "y" values are used to calculate the 21 "x" values. (The difference in "y" values is greater than the difference in "x" values.)  This will return 21 x, y, and pixel colours in the array.
If the "x" values were used, there would only be 4 calculations made from 10 to 13, which would return a 4 element array.  

#include <Array.au3>
#include <WinAPIGdi.au3>

; Using $scale in position calculations is sometimes necessary when, under the system's
; "Display" settings, the "Scale and layout" parameter is not 100%.
; When the "Scale and layout" parameter is 100%, this $scale value = 1.
Global $scale = _WinAPI_EnumDisplaySettings('', $ENUM_CURRENT_SETTINGS)[0] / @DesktopWidth

Local $Pt1[2] = [52, 28]
Local $Pt2[2] = [50, 60]
_ArrayDisplay(_PixelsInLineToArray($Pt1[0], $Pt1[1], $Pt2[0], $Pt2[1]))


; Parameters:-
;             $Pt1X, $Pt1Y - are the x and y coordinates of point 1
;             $Pt2X, $Pt2Y - are the x and y coordinates of point 2
; Returns: A 2D array:-
;                      $aArray[0] contains the X coordinate of the pixel
;                      $aArray[1] contains the Y coordinate of the pixel
;                      $aArray[3] contains the colour of the pixel in 0xRRGGBB 24bit hex. colour format. (This is what PixelGetColor returns)
;                          Where, "RR" (0-255) Red channel, "GG" (0-255) Green channel, "BB" (0-255) Blue channel.
;                          For 32bit GDIPlus hex colour format is 0xAARRGGBB.  Where. "AA" (0-255) is Alpha channel (transparency).
;                          Examples of 0xRRGGBB 24bit hex. colour format:- 0xFFFFFF is white, 0x000000 is black, 0xFF0000 is red, 0x00FF00 is green.
;
Func _PixelsInLineToArray($Pt1X, $Pt1Y, $Pt2X, $Pt2Y)
    If Abs($Pt1X - $Pt2X) > Abs($Pt1Y - $Pt2Y) Then
        If $Pt1X > $Pt2X Then ; If Abs($Pt1X - $Pt2X) has the greater distance, make Point 2 "x" value have the larger value.
            Local $TempX, $TempY
            $TempX = $Pt1X
            $TempY = $Pt1Y
            $Pt1X = $Pt2X
            $Pt1Y = $Pt2Y
            $Pt2X = $TempX
            $Pt2Y = $TempY
        EndIf
        $m = ($Pt2Y - $Pt1Y) / ($Pt2X - $Pt1X) ; line's gradient
        ; y - y1 = $m(x - x1) ; Equation for a straight line

        Local $aArray[($Pt2X - $Pt1X) + 1][3]
        For $x = $Pt1X To $Pt2X ; Note: $Pt2X has a greater value than $Pt1X.
            $aArray[$x - $Pt1X][0] = $x
            $aArray[$x - $Pt1X][1] = Round(($m * $x) - ($m * $Pt1X) + $Pt1Y, 0) ; y = $m(x - x1) + y1  (y value from equation for a straight line)
            $aArray[$x - $Pt1X][2] = "0x" & Hex(PixelGetColor($aArray[$x - $Pt1X][0] * $scale, $aArray[$x - $Pt1X][1] * $scale), 6)
        Next
        Return $aArray
    Else
        If $Pt1Y > $Pt2Y Then ; If Abs($Pt1Y - $Pt2Y) has the greater distance, make Point 2 "y" value have the larger value.
            Local $TempX, $TempY
            $TempX = $Pt1X
            $TempY = $Pt1Y
            $Pt1X = $Pt2X
            $Pt1Y = $Pt2Y
            $Pt2X = $TempX
            $Pt2Y = $TempY
        EndIf
        $m = ($Pt2Y - $Pt1Y) / ($Pt2X - $Pt1X) ; line's gradient

        Local $aArray[$Pt2Y - $Pt1Y + 1][3]
        For $y = $Pt1Y To $Pt2Y ; Note: $Pt2Y has a greater value than $Pt1Y
            $aArray[$y - $Pt1Y][0] = Round(($y / $m) - ($Pt1Y / $m) + $Pt1X, 0) ; x = (y - y1)/$m + x1 (x value from equation for a straight line)
            $aArray[$y - $Pt1Y][1] = $y
            $aArray[$y - $Pt1Y][2] = "0x" & Hex(PixelGetColor($aArray[$y - $Pt1Y][0] * $scale, $aArray[$y - $Pt1Y][1] * $scale), 6)
        Next
        Return $aArray
    EndIf
EndFunc   ;==>_PixelsInLineToArray

 

Share this post


Link to post
Share on other sites

I used "UEZ" tool "show Histogram" as a starting point for my approach:  https://bit.ly/2GXZMZ9. The histograms are a "nice to have" feature for me, and everything is nicely done. 

I am about to implement "Bresenham's line algorithm" (https://bit.ly/2RGh2Gx) to get the pixels in the line. Once done, I will implement drawing the line based on the points in the array - and then compare our results with one another. I will come back to you soon.

 

 

 

 

Share this post


Link to post
Share on other sites

Hello Malkey,

I realized by now that you implemented the Bresenham's formula. :-)   I used the function

For $k = 1 to UBound($aArray,1)-1 Step 1
    _GDIPlus_GraphicsFillRect( $hGraphics, $aArray[$k][0],  $aArray[$k][1], 1,1 )
Next

to draw the line from the array - just as a visual control. It seems correct.

But I struggle with the color code, although you have inserted a nice description:

Quote

 Where, "RR" (0-255) Red channel, "GG" (0-255) Green channel, "BB" (0-255) Blue channel.
;                          For 32bit GDIPlus hex colour format is 0xAARRGGBB.  Where. "AA" (0-255) is Alpha channel (transparency).
;                          Examples of 0xRRGGBB 24bit hex. colour format:- 0xFFFFFF is white, 0x000000 is black, 0xFF0000 is red, 0x00FF00 is green.
 

I am currently only treating grayscale images. So in case, the user opens a color image, it will get converted to grayscale prior analysis.*

In this case, the values for R, G and B are identical. So the information I am looking for is actually contained in each channel. My goal is to get the gray values in a scale of 0-255. So, hexadecimal is not the right format for my purposes. This is why I changed your code from

$aArray[$y - $Pt1Y][2] = "0x" & Hex(PixelGetColor($aArray[$y - $Pt1Y][0] * $scale, $aArray[$y - $Pt1Y][1] * $scale), 6)

 to

$aArray[$y - $Pt1Y][2] = StringRight(PixelGetColor($aArray[$x - $Pt1X][0] * $scale, $aArray[$x - $Pt1X][1] * $scale),2)

How do I proceed? How to apply the scale that I am looking for?

Thank you all for your support!

 

 

*I read in an post that the grayscale conversion by GDIPLUS is not very good. The post was quite old - did the function improve? Would you recommend to use another program to perform the conversion? I could send a command to IrfanView, for example....

 

 

Share this post


Link to post
Share on other sites
Posted (edited)

See the comments in the attached example about the color channel's value.  

To address your concerns about grayscale conversion quality, see the example under the _GDIPlus_ColorMatrixCreateGrayScale function in the AutoIt help file. 

; This gets the 24 bit pixel colour in  hex. colour format 0xRRGGBB
; These three separate colour channels, Red, Green, and Blue each range hexadeciminally from "00" to "FF" (0-255).
ConsoleWrite("0x" & Hex(PixelGetColor(100, 150), 6) & @CRLF) ; Example return 0xF0F4F9

; This gets the pixel's far right 8 bit colour channel, Blue, in hexadeciminal (base 16).
ConsoleWrite("0x" & StringRight(Hex(PixelGetColor(100, 150), 6), 2) & @CRLF) ; Example return 0xF9

; This gets the pixel's far right 8 bit colour channel, Blue, in deciminal (base 10).
ConsoleWrite(Dec(StringRight(Hex(PixelGetColor(100, 150), 6), 2)) & @CRLF) ; Example return 249

; Replacement line for script.
; $aArray[$y - $Pt1Y][2] = Dec(StringRight(Hex(PixelGetColor($aArray[$y - $Pt1Y][0] * $scale, $aArray[$y - $Pt1Y][1] * $scale), 6), 2))

 

Edited by Malkey
Added link to _GDIPlus_ColorMatrixCreateGrayScale

Share this post


Link to post
Share on other sites

Thank you very much! Works like a charm :D

I am using _GDIPlus_ColorMatrixCreateGrayScale - function. The GDIPlus.au3 calls a dll - meaning I can't track down how the matrix is setup. Would be nice to know how the red-, green- and blue channels get weighted during the conversion.

But I can compare with the results of GDIPlus and IrfanView at a later stage.

---

 

As I mentioned earlier, I use _GDIPlus_GraphicsDrawLine and _GDIPlus_GraphicsFillRect to draw lines. These lines are drawn on top of an image  that I opened with _GDIPlus_GraphicsDrawImageRectRect.

Applying the function _GDIPlus_ImageSaveToFile does not save the line along with the image - only the original image itself. In order to get a snapshot of both, image and line, I've used a workaround so far: I am taking a screenshot with autoit. But the soution not very convenient and fails easily. Is there a more elegant way to save image and line together in a jpg?

 

 

 

 

Share this post


Link to post
Share on other sites
Posted (edited)

When this example first starts the values in the 5x5 gray-scale color matrix are displayed.

To use this example:- 
Press left mouse button down, drag and move the mouse, then release the left mouse button to draw a line.
Press middle mouse button or Ctrl key to clear lines,
Press Alt+"s" to save the image with the last line drawn. 

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <ScreenCapture.au3>
#include <WinAPIHObj.au3>
#include <WindowsConstants.au3>
#include <Array.au3>
#include <Misc.au3>

Global $scale = _WinAPI_EnumDisplaySettings('', $ENUM_CURRENT_SETTINGS)[0] / @DesktopWidth

Example()

Func Example()
    Local $dll = DllOpen("user32.dll"), $aMPosNew
    If Not FileExists(@ScriptDir & "\cmy-circles.jpg") Then _
            InetGet("http://www.epi-centre.com/basics/color/images/cmy-circles.jpg", _
            @ScriptDir & "\cmy-circles.jpg")

    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\cmy-circles.jpg")
    Local $iWidth = _GDIPlus_ImageGetWidth($hImage)
    Local $iHeight = _GDIPlus_ImageGetHeight($hImage)

    Local $hGUI = GUICreate("GDI+ Example (" & @ScriptName & ")", $iWidth, $iHeight, 10, 10, $WS_POPUP) ;create a test GUI
    GUISetState(@SW_SHOW)

    Local $hGraphicGUI = _GDIPlus_GraphicsCreateFromHWND($hGUI) ;create a graphics object from a window handle
    Local $hBM = _GDIPlus_BitmapCreateFromGraphics($iWidth, $iHeight, $hGraphicGUI) ;create a Bitmap object based on a graphics object
    Local $hGraphicBM = _GDIPlus_ImageGetGraphicsContext($hBM) ;get the graphics context of the image / bitmap to draw on image / bitmap

    _GDIPlus_GraphicsSetSmoothingMode($hGraphicBM, $GDIP_SMOOTHINGMODE_HIGHQUALITY) ;sets the graphics object rendering quality (antialiasing)

    Local $hIA = _GDIPlus_ImageAttributesCreate() ;create an ImageAttribute object
    Local $tColorMatrix = _GDIPlus_ColorMatrixCreateGrayScale() ;create greyscale color matrix
    _GDIPlus_ImageAttributesSetColorMatrix($hIA, 0, True, $tColorMatrix) ;set greyscale color matrix

    _GDIPlus_GraphicsClear($hGraphicBM, 0xFF000000)
    _GDIPlus_GraphicsDrawImageRectRect($hGraphicBM, $hImage, 0, 0, $iWidth, $iHeight, 0, 0, $iWidth, $iHeight, $hIA) ;draw the bitmap while applying the color adjustment
    _GDIPlus_GraphicsDrawImageRect($hGraphicGUI, $hBM, 0, 0, $iWidth, $iHeight)
    Local $hPen = _GDIPlus_PenCreate(0xffff0000, 2)

    _DisplayColorMatrix($tColorMatrix)

    Do
        Select
            Case _IsPressed("01", $dll) ; Left mouse button down - Drag and move mouse - Release left mouse button to draw a line
                If WinExists("Pixels", "") Then WinClose("Pixels", "")
                $aMPos = MouseGetPos()
                Do
                    Sleep(10)
                Until Not _IsPressed("01", $dll)
                $aMPosNew = MouseGetPos()
                Local $aArr = _PixelsInLineToArray($aMPos[0], $aMPos[1], $aMPosNew[0], $aMPosNew[1])
                If UBound($aArr) > 1 Then
                    _GDIPlus_GraphicsDrawLine($hGraphicGUI, $aMPosNew[0], $aMPosNew[1], $aMPos[0], $aMPos[1], $hPen)
                    _ArrayDisplay($aArr, "Pixels")
                EndIf
            Case _IsPressed("04", $dll) Or _IsPressed("11", $dll) ; Press middle mouse button, or, Ctrl key to clear lines
                _GDIPlus_GraphicsClear($hGraphicBM)
                _GDIPlus_GraphicsClear($hGraphicGUI)
                _GDIPlus_GraphicsDrawImageRectRect($hGraphicBM, $hImage, 0, 0, $iWidth, $iHeight, 0, 0, $iWidth, $iHeight, $hIA) ;draw the bitmap while applying the color adjustment
                _GDIPlus_GraphicsDrawImageRect($hGraphicGUI, $hBM, 0, 0, $iWidth, $iHeight)
            Case _IsPressed("12", $dll) And _IsPressed("53", $dll) ; Press Alt + "s" to save image
                _GDIPlus_GraphicsDrawLine($hGraphicBM, $aMPosNew[0], $aMPosNew[1], $aMPos[0], $aMPos[1], $hPen)
                _GDIPlus_ImageSaveToFile($hBM, @ScriptDir & "\SCTesta.jpg")
                ShellExecuteWait(@ScriptDir & "\SCTesta.jpg")

        EndSelect
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    ;cleanup GDI+ resources
    _GDIPlus_ImageAttributesDispose($hIA)
    _GDIPlus_GraphicsDispose($hGraphicGUI)
    _GDIPlus_GraphicsDispose($hGraphicBM)
    _GDIPlus_PenDispose($hPen)
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_BitmapDispose($hBM)
    _GDIPlus_Shutdown()
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _DisplayColorMatrix($tCMStruct) ; See $tagGDIPCOLORMATRIX AutoIt Help File
    Local $aArray[5][5]
    For $i = 1 To 5
        For $j = 1 To 5
            $aArray[$i - 1][$j - 1] = DllStructGetData($tCMStruct, 1, (($i - 1) * 5) + $j)
        Next
    Next
    _ArrayDisplay($aArray, "Display ColorMatrix")
EndFunc   ;==>_DisplayColorMatrix

; Parameters:-
;             $Pt1X, $Pt1Y - are the x and y coordinates of point 1
;             $Pt2X, $Pt2Y - are the x and y coordinates of point 2
; Returns: A 2D array:-
;                      $aArray[0] contains the X coordinate of the pixel
;                      $aArray[1] contains the Y coordinate of the pixel
;                      $aArray[3] contains the colour of the pixel in 0xRRGGBB hex. colour format.
;
Func _PixelsInLineToArray($Pt1X, $Pt1Y, $Pt2X, $Pt2Y)
    ConsoleWrite($Pt1X & "  " & $Pt1Y & "  " & $Pt2X & "  " & $Pt2Y & @CRLF)
    If Abs($Pt1X - $Pt2X) > Abs($Pt1Y - $Pt2Y) Then
        If $Pt1X > $Pt2X Then ; If Abs($Pt1X - $Pt2X) has the greater distance, make Point 2 "x" value have the larger value.
            Local $TempX, $TempY
            $TempX = $Pt1X
            $TempY = $Pt1Y
            $Pt1X = $Pt2X
            $Pt1Y = $Pt2Y
            $Pt2X = $TempX
            $Pt2Y = $TempY
        EndIf
        $m = ($Pt2Y - $Pt1Y) / ($Pt2X - $Pt1X) ; line's gradient
        ; y - y1 = $m(x - x1) ; Equation for a straight line
        ; x = x1 +(y - y1)/$m

        Local $aArray[($Pt2X - $Pt1X) + 1][3]
        For $x = $Pt1X To $Pt2X ; Note: $Pt2X has a greater value than $Pt1X.
            $aArray[$x - $Pt1X][0] = $x
            $aArray[$x - $Pt1X][1] = Round(($m * $x) - ($m * $Pt1X) + $Pt1Y, 0)
            $aArray[$x - $Pt1X][2] = "0x" & Hex(PixelGetColor($aArray[$x - $Pt1X][0] * $scale, $aArray[$x - $Pt1X][1] * $scale), 6)
        Next
        Return $aArray
    Else
        If $Pt1Y > $Pt2Y Then ; If Abs($Pt1Y - $Pt2Y) has the greater distance, make Point 2 "y" value have the larger value.
            Local $TempX, $TempY
            $TempX = $Pt1X
            $TempY = $Pt1Y
            $Pt1X = $Pt2X
            $Pt1Y = $Pt2Y
            $Pt2X = $TempX
            $Pt2Y = $TempY
        EndIf
        $m = ($Pt2Y - $Pt1Y) / ($Pt2X - $Pt1X) ; line's gradient
        ; y - y1 = $m(x - x1) ; Equation for a straight line

        Local $aArray[$Pt2Y - $Pt1Y + 1][3]
        For $y = $Pt1Y To $Pt2Y ; Note: $Pt2Y has a greater value than $Pt1Y
            $aArray[$y - $Pt1Y][0] = Round(($y / $m) - ($Pt1Y / $m) + $Pt1X, 0)
            $aArray[$y - $Pt1Y][1] = $y
            $aArray[$y - $Pt1Y][2] = "0x" & Hex(PixelGetColor($aArray[$y - $Pt1Y][0] * $scale, $aArray[$y - $Pt1Y][1] * $scale), 6)
        Next
        Return $aArray
    EndIf
EndFunc   ;==>_PixelsInLineToArray

 

Edited by Malkey
Updated import image to GUI.

Share this post


Link to post
Share on other sites
On 1/9/2019 at 1:54 PM, Malkey said:

When this example first starts the values in the 5x5 gray-scale color matrix are displayed.

To use this example:- 
Press left mouse button down, drag and move the mouse, then release the left mouse button to draw a line.
Press middle mouse button or Ctrl key to clear lines,
Press Alt+"s" to save the image with the last line drawn. 

#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <ScreenCapture.au3>
#include <WinAPIHObj.au3>
#include <WindowsConstants.au3>
#include <Array.au3>
#include <Misc.au3>

Global $scale = _WinAPI_EnumDisplaySettings('', $ENUM_CURRENT_SETTINGS)[0] / @DesktopWidth

Example()

Func Example()
    Local $dll = DllOpen("user32.dll"), $aMPosNew
    If Not FileExists(@ScriptDir & "\cmy-circles.jpg") Then _
            InetGet("http://www.epi-centre.com/basics/color/images/cmy-circles.jpg", _
            @ScriptDir & "\cmy-circles.jpg")

    _GDIPlus_Startup()
    Local $hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\cmy-circles.jpg")
    Local $iWidth = _GDIPlus_ImageGetWidth($hImage)
    Local $iHeight = _GDIPlus_ImageGetHeight($hImage)

    Local $hGUI = GUICreate("GDI+ Example (" & @ScriptName & ")", $iWidth, $iHeight, 10, 10, $WS_POPUP) ;create a test GUI
    GUISetState(@SW_SHOW)

    Local $hGraphicGUI = _GDIPlus_GraphicsCreateFromHWND($hGUI) ;create a graphics object from a window handle
    Local $hBM = _GDIPlus_BitmapCreateFromGraphics($iWidth, $iHeight, $hGraphicGUI) ;create a Bitmap object based on a graphics object
    Local $hGraphicBM = _GDIPlus_ImageGetGraphicsContext($hBM) ;get the graphics context of the image / bitmap to draw on image / bitmap

    _GDIPlus_GraphicsSetSmoothingMode($hGraphicBM, $GDIP_SMOOTHINGMODE_HIGHQUALITY) ;sets the graphics object rendering quality (antialiasing)

    Local $hIA = _GDIPlus_ImageAttributesCreate() ;create an ImageAttribute object
    Local $tColorMatrix = _GDIPlus_ColorMatrixCreateGrayScale() ;create greyscale color matrix
    _GDIPlus_ImageAttributesSetColorMatrix($hIA, 0, True, $tColorMatrix) ;set greyscale color matrix

    _GDIPlus_GraphicsClear($hGraphicBM, 0xFF000000)
    _GDIPlus_GraphicsDrawImageRectRect($hGraphicBM, $hImage, 0, 0, $iWidth, $iHeight, 0, 0, $iWidth, $iHeight, $hIA) ;draw the bitmap while applying the color adjustment
    _GDIPlus_GraphicsDrawImageRect($hGraphicGUI, $hBM, 0, 0, $iWidth, $iHeight)
    Local $hPen = _GDIPlus_PenCreate(0xffff0000, 2)

    _DisplayColorMatrix($tColorMatrix)

    Do
        Select
            Case _IsPressed("01", $dll) ; Left mouse button down - Drag and move mouse - Release left mouse button to draw a line
                If WinExists("Pixels", "") Then WinClose("Pixels", "")
                $aMPos = MouseGetPos()
                Do
                    Sleep(10)
                Until Not _IsPressed("01", $dll)
                $aMPosNew = MouseGetPos()
                Local $aArr = _PixelsInLineToArray($aMPos[0], $aMPos[1], $aMPosNew[0], $aMPosNew[1])
                If UBound($aArr) > 1 Then
                    _GDIPlus_GraphicsDrawLine($hGraphicGUI, $aMPosNew[0], $aMPosNew[1], $aMPos[0], $aMPos[1], $hPen)
                    _ArrayDisplay($aArr, "Pixels")
                EndIf
            Case _IsPressed("04", $dll) Or _IsPressed("11", $dll) ; Press middle mouse button, or, Ctrl key to clear lines
                _GDIPlus_GraphicsClear($hGraphicBM)
                _GDIPlus_GraphicsClear($hGraphicGUI)
                _GDIPlus_GraphicsDrawImageRectRect($hGraphicBM, $hImage, 0, 0, $iWidth, $iHeight, 0, 0, $iWidth, $iHeight, $hIA) ;draw the bitmap while applying the color adjustment
                _GDIPlus_GraphicsDrawImageRect($hGraphicGUI, $hBM, 0, 0, $iWidth, $iHeight)
            Case _IsPressed("12", $dll) And _IsPressed("53", $dll) ; Press Alt + "s" to save image
                _GDIPlus_GraphicsDrawLine($hGraphicBM, $aMPosNew[0], $aMPosNew[1], $aMPos[0], $aMPos[1], $hPen)
                _GDIPlus_ImageSaveToFile($hBM, @ScriptDir & "\SCTesta.jpg")
                ShellExecuteWait(@ScriptDir & "\SCTesta.jpg")

        EndSelect
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    ;cleanup GDI+ resources
    _GDIPlus_ImageAttributesDispose($hIA)
    _GDIPlus_GraphicsDispose($hGraphicGUI)
    _GDIPlus_GraphicsDispose($hGraphicBM)
    _GDIPlus_PenDispose($hPen)
    _GDIPlus_BitmapDispose($hImage)
    _GDIPlus_BitmapDispose($hBM)
    _GDIPlus_Shutdown()
    GUIDelete($hGUI)
EndFunc   ;==>Example

Func _DisplayColorMatrix($tCMStruct) ; See $tagGDIPCOLORMATRIX AutoIt Help File
    Local $aArray[5][5]
    For $i = 1 To 5
        For $j = 1 To 5
            $aArray[$i - 1][$j - 1] = DllStructGetData($tCMStruct, 1, (($i - 1) * 5) + $j)
        Next
    Next
    _ArrayDisplay($aArray, "Display ColorMatrix")
EndFunc   ;==>_DisplayColorMatrix

; Parameters:-
;             $Pt1X, $Pt1Y - are the x and y coordinates of point 1
;             $Pt2X, $Pt2Y - are the x and y coordinates of point 2
; Returns: A 2D array:-
;                      $aArray[0] contains the X coordinate of the pixel
;                      $aArray[1] contains the Y coordinate of the pixel
;                      $aArray[3] contains the colour of the pixel in 0xRRGGBB hex. colour format.
;
Func _PixelsInLineToArray($Pt1X, $Pt1Y, $Pt2X, $Pt2Y)
    ConsoleWrite($Pt1X & "  " & $Pt1Y & "  " & $Pt2X & "  " & $Pt2Y & @CRLF)
    If Abs($Pt1X - $Pt2X) > Abs($Pt1Y - $Pt2Y) Then
        If $Pt1X > $Pt2X Then ; If Abs($Pt1X - $Pt2X) has the greater distance, make Point 2 "x" value have the larger value.
            Local $TempX, $TempY
            $TempX = $Pt1X
            $TempY = $Pt1Y
            $Pt1X = $Pt2X
            $Pt1Y = $Pt2Y
            $Pt2X = $TempX
            $Pt2Y = $TempY
        EndIf
        $m = ($Pt2Y - $Pt1Y) / ($Pt2X - $Pt1X) ; line's gradient
        ; y - y1 = $m(x - x1) ; Equation for a straight line
        ; x = x1 +(y - y1)/$m

        Local $aArray[($Pt2X - $Pt1X) + 1][3]
        For $x = $Pt1X To $Pt2X ; Note: $Pt2X has a greater value than $Pt1X.
            $aArray[$x - $Pt1X][0] = $x
            $aArray[$x - $Pt1X][1] = Round(($m * $x) - ($m * $Pt1X) + $Pt1Y, 0)
            $aArray[$x - $Pt1X][2] = "0x" & Hex(PixelGetColor($aArray[$x - $Pt1X][0] * $scale, $aArray[$x - $Pt1X][1] * $scale), 6)
        Next
        Return $aArray
    Else
        If $Pt1Y > $Pt2Y Then ; If Abs($Pt1Y - $Pt2Y) has the greater distance, make Point 2 "y" value have the larger value.
            Local $TempX, $TempY
            $TempX = $Pt1X
            $TempY = $Pt1Y
            $Pt1X = $Pt2X
            $Pt1Y = $Pt2Y
            $Pt2X = $TempX
            $Pt2Y = $TempY
        EndIf
        $m = ($Pt2Y - $Pt1Y) / ($Pt2X - $Pt1X) ; line's gradient
        ; y - y1 = $m(x - x1) ; Equation for a straight line

        Local $aArray[$Pt2Y - $Pt1Y + 1][3]
        For $y = $Pt1Y To $Pt2Y ; Note: $Pt2Y has a greater value than $Pt1Y
            $aArray[$y - $Pt1Y][0] = Round(($y / $m) - ($Pt1Y / $m) + $Pt1X, 0)
            $aArray[$y - $Pt1Y][1] = $y
            $aArray[$y - $Pt1Y][2] = "0x" & Hex(PixelGetColor($aArray[$y - $Pt1Y][0] * $scale, $aArray[$y - $Pt1Y][1] * $scale), 6)
        Next
        Return $aArray
    EndIf
EndFunc   ;==>_PixelsInLineToArray

 

2

Malkey,

once again: thank you very much for your help.

I always wonder how you guys manage to post entire scripts as replies: takes me many days to get my stuff working, and you seem to be able to write withing 10min... :o

I am currently stuck and working on another problem, but will soon implement your solution. 

Best regards,

dejhost

Share this post


Link to post
Share on other sites

Do you know the (old) Star-Wars scene where a X-wing tries to defeat the death-star? Trying to get into the right position to fire the torpedo, the pilot repeats "I'm almost there...I'm almost there...". I just feel the same way ;-)   Hopefully in the end more lucky than the X-wing pilot, though...

A remaining problem I have: The two functions "PixelGetColor()" and "_GDIPlus_GraphicsDrawLine()" (used to save the image together with the line) seem to use a different Coordinate system than the rest of my program. I placed

Opt("PixelCoordMode", 0) ;1=absolute, 0=relative, 2=client

in the beginning of the subroutines, and tried all three options. None of the options results in correct output. The colors extracted show different values than expected, and the image saved shows the line that was drawn by the user at the wrong position.

I believe it has something to do with the image position and the image dimension that I handle wrong(?) Here is the relevant code. It's not the complete code (which is too untidy to be published :'( . Yes. Far more untidy than the code below...).

Global $scale = _WinAPI_EnumDisplaySettings('', $ENUM_CURRENT_SETTINGS)[0] / @DesktopWidth
Global $hUserDll = DllOpen("user32.dll")
Global $hGraphics, $hGraphicBM, $pos, $iWidth, $iHeight, $hBM, $hIA
Global $scale = 1;_WinAPI_EnumDisplaySettings('', $ENUM_CURRENT_SETTINGS)[0] / @DesktopWidth
Global $aArray

Opt("MustDeclareVars", 1)
Opt("MouseCoordMode", 1)
Opt("WinTitleMatchMode", 2) ;1=start, 2=subStr, 3=exact, 4=advanced, -1 to -4=Nocase


Func _LoadImage()
;Load Image
    $sFile = FileOpenDialog("Select an image", "", "Images (*.bmp;*.png;*.jpg;*.gif;*.tif)", 1)
    $Path =_Pathsplit($sFile, $sDrive, $sDir, $sFileName, $sExtension)
        If @error Or Not FileExists($sFile) Then MsgBox($MB_SYSTEMMODAL, "File doesn't exist - 267", "Error opening file " & $Path)
    $hImage = _GDIPlus_ImageLoadFromFile($sFile)
    _WinAPI_DeleteObject($hImage) ;release GDI bitmap resource because not needed anymore
    $iWidth = _GDIPlus_ImageGetWidth($hImage)
    $iHeight = _GDIPlus_ImageGetHeight($hImage)

;== DRAW IMAGE, APPLYING GRAYSCALE IF NECESSARY
; GUI
;~  Global $iWidth = @DesktopWidth*3/4;
;~  Global $iHeight = @DesktopHeight*3/4;_GDIPlus_ImageGetHeight($hImage) * $iWidth / _GDIPlus_ImageGetWidth($hImage)
    Global $hGui2 = GUICreate("GDI+ v1.1 (" & @ScriptName & ")", $iWidth, $iHeight)
    GUISetState(@SW_SHOW)

;GREYSCALE
     $hGraphics = _GDIPlus_GraphicsCreateFromHWND($hGUI2) ;create a graphics object from a window handle
     $hBM = _GDIPlus_BitmapCreateFromGraphics($iWidth, $iHeight, $hGraphics)
        $hGraphicBM = _GDIPlus_ImageGetGraphicsContext($hBM)
         $hIA = _GDIPlus_ImageAttributesCreate() ;create an ImageAttribute object
    Global $tColorMatrix = _GDIPlus_ColorMatrixCreateGrayScale() ;create greyscale color matrix
    _GDIPlus_ImageAttributesSetColorMatrix($hIA, 0, True, $tColorMatrix) ;set greyscale color matrix
    WinClose(StringRegExpReplace($sFile, "^.*\\|\..*$", ""))
    If NOT _IsImageGrayScale($sFile) Then
        _GDIPlus_GraphicsDrawImageRectRect($hGraphics, $hImage, 0, 0, _GDIPlus_ImageGetWidth($hImage), _GDIPlus_ImageGetHeight($hImage), 0, 0, $iWidth, $iHeight,$hiA);, 0, 0, 1400, 900,$hIA ) ;draw the bitmap while applying the color adjustment
    Else
        _GDIPlus_GraphicsDrawImageRectRect($hGraphics, $hImage, 0, 0, _GDIPlus_ImageGetWidth($hImage), _GDIPlus_ImageGetHeight($hImage), 0, 0, $iWidth, $iHeight);, 0, 0, 1400, 900,$hIA ) ;draw the bitmap while applying the color adjustment
    EndIf

Return $Path
EndFunc   ;==>LoadImage


Func _SaveScreenshot()
;~  $iWidth = _GDIPlus_ImageGetWidth($hImage)
;~  $iHeight = _GDIPlus_ImageGetHeight($hImage)
;~  Local $hGUI = GUICreate("GDI+ Example (" & @ScriptName & ")", $iWidth, $iHeight, 10, 10, $WS_POPUP) ;create a test GUI

    Local $hGraphicGUI = _GDIPlus_GraphicsCreateFromHWND($hGUI2) ;create a graphics object from a window handle
    Local $hBM = _GDIPlus_BitmapCreateFromGraphics($iWidth, $iHeight, $hGraphicGUI) ;create a Bitmap object based on a graphics object
    Local $hGraphicBM = _GDIPlus_ImageGetGraphicsContext($hBM) ;get the graphics context of the image / bitmap to draw on image / bitmap

    _GDIPlus_GraphicsClear($hGraphicBM, 0xFF000000)
    _GDIPlus_GraphicsDrawImageRectRect($hGraphicBM, $hImage, 0, 0, $iWidth, $iHeight, 0, 0, $iWidth, $iHeight, $hIA) ;draw the bitmap while applying the color adjustment
    _GDIPlus_GraphicsDrawImageRect($hGraphicGUI, $hBM, 0, 0, $iWidth, $iHeight)
    Local $hPen = _GDIPlus_PenCreate(0xffff0000, 2)

    _GDIPlus_GraphicsDrawLine($hGraphicBM, $Pt1[0], $Pt1[1], $Pt2[0], $Pt2[1], $hPen)
    _GDIPlus_ImageSaveToFile($hBM, @ScriptDir & "\screenshot.jpg")
;~     ShellExecuteWait(@ScriptDir & "\screenshot.jpg")Sc
EndFunc ;==>_SaveScreenshot


Func _PixelsInLineToArray($Pt1X, $Pt1Y, $Pt2X, $Pt2Y)
   Local $m

    ProgressOn("Progress of Pixel processing", "", "0%", 100, 100) ; Display progressbar

    If Abs($Pt1X - $Pt2X) > Abs($Pt1Y - $Pt2Y) Then
        If $Pt1X > $Pt2X Then ; If Abs($Pt1X - $Pt2X) has the greater distance, make Point 2 "x" value have the larger value.
            Local $TempX, $TempY
            $TempX = $Pt1X
            $TempY = $Pt1Y
            $Pt1X = $Pt2X
            $Pt1Y = $Pt2Y
            $Pt2X = $TempX
            $Pt2Y = $TempY
        EndIf
        $m = ($Pt2Y - $Pt1Y) / ($Pt2X - $Pt1X) ; line's gradient
        ; y - y1 = $m(x - x1) ; Equation for a straight line

        Global $aArray[($Pt2X - $Pt1X) + 1][3]
        For $x = $Pt1X To $Pt2X ; Note: $Pt2X has a greater value than $Pt1X.
            ;msgbox(0,"progress value",Floor($x / $Pt2X * 100) & "%",1)

            $aArray[$x - $Pt1X][0] = $x
            $aArray[$x - $Pt1X][1] = Round(($m * $x) - ($m * $Pt1X) + $Pt1Y, 0) ; y = $m(x - x1) + y1  (y value from equation for a straight line)
            $aArray[$x - $Pt1X][2] = Dec(StringRight(Hex(PixelGetColor($aArray[$x - $Pt1X][0] * $scale, $aArray[$x - $Pt1X][1] * $scale), 6), 2))
            ;$aArray[$x - $Pt1X][2] = "0x" & Hex(PixelGetColor($aArray[$x - $Pt1X][0] * $scale, $aArray[$x - $Pt1X][1] * $scale), 6)
            ProgressSet(Floor($x / $Pt2X * 100),"%","soon finished calculating") ;increase Progressbar
        Next
        ProgressSet(100, "Done", "operation completed")
        Sleep(1000)
        ProgressOff()
        ;_ArrayDisplay($aArray)
        Return $aArray
    Else
        If $Pt1Y > $Pt2Y Then ; If Abs($Pt1Y - $Pt2Y) has the greater distance, make Point 2 "y" value have the larger value.
            Local $TempX, $TempY
            $TempX = $Pt1X
            $TempY = $Pt1Y
            $Pt1X = $Pt2X
            $Pt1Y = $Pt2Y
            $Pt2X = $TempX
            $Pt2Y = $TempY
        EndIf
        $m = ($Pt2Y - $Pt1Y) / ($Pt2X - $Pt1X) ; line's gradient

        Global $aArray[$Pt2Y - $Pt1Y + 1][3]
        For $y = $Pt1Y To $Pt2Y ; Note: $Pt2Y has a greater value than $Pt1Y
            ;msgbox(0,"progress value",Floor($y / $Pt2y * 100)& "%",1)
            $aArray[$y - $Pt1Y][0] = Round(($y / $m) - ($Pt1Y / $m) + $Pt1X, 0) ; x = (y - y1)/$m + x1 (x value from equation for a straight line)
            $aArray[$y - $Pt1Y][1] = $y
            $aArray[$y - $Pt1Y][2] = Dec(StringRight(Hex(PixelGetColor($aArray[$y - $Pt1Y][0] * $scale, $aArray[$y - $Pt1Y][1] * $scale), 6), 2))
            ;$aArray[$y - $Pt1Y][2] = "0x" & Hex(PixelGetColor($aArray[$y - $Pt1Y][0] * $scale, $aArray[$y - $Pt1Y][1] * $scale), 6)
            ProgressSet(Floor($y / $Pt2Y *100),"%","soon finished calculating")

        Next
        ProgressSet(100, "Done", "operation completed")
        Sleep(1000)
        ProgressOff()
        ;_ArrayDisplay($aArray)
        Return $aArray
    EndIf
EndFunc   ;==>_PixelsInLineToArray

I cannot get my head around this, because I do not yet understand the code in all details yet. Could somebody please help me out?

Edited by dejhost
Improved explanations

Share this post


Link to post
Share on other sites
2 hours ago, Nine said:

use _GDIPlus_BitmapGetPixel instead

I implemented the _GDIPlus_BitmapGetPixel into _PixelsInLineToArray. It appears that the _PixelsInLineToArray has now the same coordinate system as _SaveScreenshot. So definatevly an improvement.

Now I need to get both consistent with what the user sees/draws. 

Share this post


Link to post
Share on other sites

Here another way to do it. I draw a bezier curve and read out the colors of that curve. You can us a line instead. Advantage is that you do not need to calculate the path of any line to read out the pixel color accordingly.

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

_GDIPlus_Startup()

Global Const $hGUI = GUICreate("Test", 512, 512)
GUISetState()

Global Const $hHBitmap = _ScreenCapture_Capture("", 0, 0, 511, 511, False), _
             $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hHBitmap), _
             $hGfx = _GDIPlus_ImageGetGraphicsContext($hImage), _
             $hImage2 = _GDIPlus_BitmapCreateFromScan0(512, 512), _
             $hGfx2 = _GDIPlus_ImageGetGraphicsContext($hImage2), _
             $hPen = _GDIPlus_PenCreate(0xFFFF0000, 8) ;change the pen size accordingly

_GDIPlus_GraphicsSetSmoothingMode($hGfx2, 0)
_GDIPlus_GraphicsDrawBezier($hGfx2, 50,  50, _
                                   100, 350, _
                                   325,  25, _
                                   450, 500, $hPen)

Global Const $hCanvas = _GDIPlus_GraphicsCreateFromHWND($hGUI)
_GDIPlus_GraphicsDrawImageRect($hCanvas, $hImage, 0, 0, 511, 511)
_GDIPlus_GraphicsDrawImageRect($hCanvas, $hImage2, 0, 0, 511, 511)
Global $aColors = GetColors($hImage2, $hImage), $i
Global $hImageResult = _GDIPlus_BitmapCreateFromScan0(512, 512, $GDIP_PXF24RGB)
For $i = 0 To UBound($aColors) - 1
    _GDIPlus_BitmapSetPixel($hImageResult, $aColors[$i][0], $aColors[$i][1], $aColors[$i][2]) ;slow - faster is to use _GDIPlus_BitmapLockBits() but I was to lazy :-)
Next
MsgBox(0, "Test", "Click OK to view result")

_GDIPlus_GraphicsDrawImageRect($hCanvas, $hImageResult, 0, 0, 511, 511)

;_ArrayDisplay($aColors)

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

_WinAPI_DeleteObject($hHBitmap)
_GDIPlus_PenDispose($hPen)
_GDIPlus_GraphicsDispose($hGfx)
_GDIPlus_ImageDispose($hImage)
_GDIPlus_GraphicsDispose($hGfx2)
_GDIPlus_ImageDispose($hImage2)
_GDIPlus_ImageDispose($hImageResult)
_GDIPlus_Shutdown()

Func GetColors($hBitmap1, $hBitmap2, $iSearchColor = 0xFFFF0000)
    Local $aDim = _GDIPlus_ImageGetDimension($hBitmap1)
    Local $iW = $aDim[0], $iH = $aDim[1]
    ;this is the bitmap with the red bezier only
    Local $tBitmapData1 = _GDIPlus_BitmapLockBits($hBitmap1, 0, 0, $iW, $iH, $GDIP_ILMREAD, $GDIP_PXF32ARGB) ;locks a portion of a bitmap for reading and writing. More infor at http://msdn.microsoft.com/en-us/library/windows/desktop/ms536298(v=vs.85).aspx
    Local $iScan01 = DllStructGetData($tBitmapData1, "Scan0") ;get scan0 (pixel data) from locked bitmap
    Local $tPixel1 = DllStructCreate("int[" & $iW * $iH & "];", $iScan01)

    ;this is the bitmap where we read the colors
    Local $tBitmapData2 = _GDIPlus_BitmapLockBits($hBitmap2, 0, 0, $iW, $iH, $GDIP_ILMREAD, $GDIP_PXF32ARGB) ;locks a portion of a bitmap for reading and writing. More infor at http://msdn.microsoft.com/en-us/library/windows/desktop/ms536298(v=vs.85).aspx
    Local $iScan02 = DllStructGetData($tBitmapData2, "Scan0") ;get scan0 (pixel data) from locked bitmap
    Local $tPixel2 = DllStructCreate("int[" & $iW * $iH & "];", $iScan02)

    Local $iPixel, $iRowOffset, $p = 0
    Local $aColors[$iW * $iH][3] ;x, y, color
    For $iY = 0 To $iH - 1
        $iRowOffset = $iY * $iW + 1
        For $iX = 0 To $iW - 1 ;get each pixel in each line and row
            $iPixel = DllStructGetData($tPixel1, 1, $iRowOffset + $iX) ;get pixel color from bezier
            If $iPixel = $iSearchColor Then
                $aColors[$p][0] = $iX
                $aColors[$p][1] = $iY
                $aColors[$p][2] = DllStructGetData($tPixel2, 1, $iRowOffset + $iX) ;save pixel color from original image without bezier
                $p += 1
            EndIf
        Next
    Next
    _GDIPlus_BitmapUnlockBits($hBitmap1, $tBitmapData1) ;unlocks a portion of a bitmap that was locked by _GDIPlus_BitmapLockBits
    _GDIPlus_BitmapUnlockBits($hBitmap2, $tBitmapData2) ;unlocks a portion of a bitmap that was locked by _GDIPlus_BitmapLockBits
    ReDim $aColors[$p - 1][3]
    Return $aColors
EndFunc

 

I don't know whether this is something your are looking for...


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
;Call the function:
_PixelsInLineToArray($Pt1[0]-$centerX, $Pt1[1]-$centerY, $Pt2[0]-$centerX, $Pt2[1]-$centerY)

@UEZ: Thank you very much. But since I was "almost there..." anyhow, I decided to bring the first approach to its well-deserved glory, first. I have of course looked at your code, but I need more time to study the "GetColors" function more closely. Will do so soon and compare both approaches.

Finally managed to fix the coordinate problem for both: saving the image and the pixel2array: Also Malkey's code saves a faulty line. Reason is that the image has a "left =10" and "top = 10" position when it get drawn. So the line that is saved is actually 10 pixels too far to the right, and 10 pixels too far down.

Im my code, the image is drawn centered. That means that both functions (getColors and pixel2array) need to get corrected accordingly:
 

$centerX = (@DesktopWidth/2)-($iWidth/2)
$centerY = (@DesktopHeight/2)-($iHeight/2)

Call of the function Pixel2Array:

_PixelsInLineToArray($Pt1[0]-$centerX, $Pt1[1]-$centerY, $Pt2[0]-$centerX, $Pt2[1]-$centerY)
$aArray[$x - $Pt1X][0] = $centerX + $x
$aArray[$x - $Pt1X][1] = $centery + Round(($m * $x) - ($m * $Pt1X) + $Pt1Y, 0) ; y = $m(x - x1) + y1  (y value from equation for a straight line)
$aArray[$x - $Pt1X][2] = Dec(StringRight(Hex(PixelGetColor(($aArray[$x - $Pt1X][0] * $scale), ($aArray[$x - $Pt1X][1] * $scale)), 6), 2))

In the function SaveImage:

_GDIPlus_GraphicsDrawLine($hGraphicBM, $Pt1[0]-$centerX, $Pt1[1]-$centerY, $Pt2[0]-$centerX, $Pt2[1]-$centerY,$hPen)

 

 

 

 

 

Edited by dejhost
more context

Share this post


Link to post
Share on other sites

Last question - just out of curiosity: would it be possible to extract the pixelcolor from a closed image?

My reason for asking: if the image has to be opened, then it might have to be resized in order to fit the screen. Only then the user can see the entire image, and indicate start- and endpoint of the line. But in this means that the extracted grayvalues do not refer to the original image - only to the resized one. Information gets lost.

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

  • Similar Content

    • By TheDcoder
      Hello everyone, I am working on a project which requires reading a few values from Excel, the catch is that I need it to be very fast... unfortunatley I found out that read operations using the supplied Excel UDF are very slow, more than 150 ms for each operation on average
      Here is my testing setup that I made:
      #include <Excel.au3> #include <MsgBoxConstants.au3> Global $iTotalTime = 0 Test() Func Test() Local $oExcel = _Excel_Open() Local $oBook = _Excel_BookAttach("Test.xlsx", "FileName", $oExcel) Local $sSheet = "Sheet1" If @error Then Return MsgBox($MB_ICONERROR, "Excel Failed", "Failed to attach to Excel") Local $iNum For $iRow = 1 To 6 Time() Local $iNum = Number(_Excel_RangeRead($oBook, $sSheet, "A" & $iRow)) If ($iNum = 1) Then ConsoleWrite("Row " & $iRow & " is 1 and value of column B is " & _Excel_RangeRead($oBook, $sSheet, "B" & $iRow)) Else ConsoleWrite("Row " & $iRow & " is not 1") EndIf ConsoleWrite(". Reading took: ") Time() Next ConsoleWrite("The whole operation took " & $iTotalTime & " milliseconds." & @CRLF) EndFunc Func Time() Local Static $hTimer Local Static $bRunning = False If $bRunning Then Local $iTime = Round(TimerDiff($hTimer), 2) $iTotalTime += $iTime ConsoleWrite($iTime & @CRLF) Else $hTimer = TimerInit() EndIf $bRunning = Not $bRunning EndFunc And Test.xlsx in CSV format:
      1,-1 -1,1 1,-1 1,1 -1,-1 1,1 Here is the actual xlsx but it should expire in a week: https://we.tl/t-EVkxGp1kc6
      And finally output from my script:
      Row 1 is 1 and value of column B is -1. Reading took: 276.06 Row 2 is not 1. Reading took: 163.36 Row 3 is 1 and value of column B is -1. Reading took: 302.58 Row 4 is 1 and value of column B is 1. Reading took: 294.65 Row 5 is not 1. Reading took: 152.33 Row 6 is 1 and value of column B is 1. Reading took: 284.92 The whole operation took 1473.9 milliseconds.  
      Taking ~1.5 seconds for reading 6 rows of data is bad for my script, which needs to run as fast as possible . It would be nice if I can bring this down to 100 ms somehow, I am not very experienced working with MS office so I thought about asking you folks for help and advice on how I can optimize my script to squeeze out every bit of performance that I can get from this script
       
      Thanks for the help in advance!
    • By SlackerAl
      I have been using some AutoIt scripts to manipulate Excel for a few weeks now. Today they stopped working. I have rebooted the PC and I'm not aware of any other significant changes. I can start and use Excel conventionally without a problem, but any attempt to create an excel object from AutoIt fails. E.g
      #include <MsgBoxConstants.au3> #include <Excel.au3> Local $oExcel = _Excel_Open() If @error Then Exit MsgBox($MB_SYSTEMMODAL, "Excel UDF: _Excel_Open Example 1", "Error creating the Excel application object." & @CRLF & "@error = " & @error & ", @extended = " & @extended) Produces:
      @error = 1
      @extended = -2147221005
      I can't find that COM error listed anywhere... Anyone have any ideas?
    • By Eggie6
      Hi,
      I'm trying to write an formula to the excel, but it actually does not write anything into the cell, any ideas?
      $formula1="=MID(B"&$utakmice&",SEARCH("-",B"&$utakmice&")-1,1)" _Excel_RangeWrite($oWorkbook, $oWorkbook.Activesheet, $formula1, "C"&$smth)  
    • By XinYoung
      Hello all,
      Preface: Column A is full of courses. Column B is full of usernames. If course (A1) exists, we check to see if username (B1) is enrolled. If user is found, the user is un-enrolled. Then Loop.
      I am working on a script that loops through an Excel file and pastes the content from A1 into a textbox in IE, does some stuff, then pastes the content from B1 into a different textbox. Then it loops around until all used rows in columns A and B have been accounted for.
      For some reason, column A loops properly but column B doesn't. B1 is pasted over and over again. So, as it loops, B1 is constantly being pasted, first accompanying A1's loop, then A2, and so on.
      The OpenExcel() func opens the Excel file the user specifies in an earlier function. It's supposed to gather the entire used range of columns A and B. The SearchCourse() func only uses column A, pasting its content into a Search tool in IE. This seems to be working fine. SearchResult() puts "Course Not Found" into column C if the search fails. If the search is successful, however, we move onto... EnterCourse(). This simply gets us to the place where column B's content comes into play. UnenrollNow(). Here, we paste the variable $_userName into a textbox. I don't know why it's always B1  Func OpenExcel() If Not WinExists($hWnd) Then MsgBox(16, "Window closed", "The Internet Explorer window was closed. Please start over.") _Exit() Else $oExcel = _Excel_Open() Global $oWorkbook = _Excel_BookOpen($oExcel, $ChosenFileName, Default, True, True) $oExcel.Sheets("CopyCourses").Activate ;~ Get all used cells in columns A and B $aSearchItems = _Excel_RangeRead($oWorkbook, 1, $oWorkbook.Sheets("CopyCourses").Usedrange.Columns("A:B")) ;~ Create the $aSearchResult array ReDim $aSearchResult[UBound($aSearchItems)] ;~ Loop through the array starting at 0 until the end of the array which is (Ubound($aSearchItems) - 1) For $i = 0 To UBound($aSearchItems) - 1 ;~ Column 0 ConsoleWrite($aSearchItems[$i][0] & @CRLF) ;~ Column 1 ConsoleWrite($aSearchItems[$i][1] & @CRLF) $aSearchResult[$i] = SearchCourse($aSearchItems[$i][0], $aSearchItems[$i][1]) Next _Excel_RangeWrite($oWorkbook, Default, $aSearchResult, "C1") ;~ When the loop is complete, run the Finished function. Finished() EndIf EndFunc ;==>OpenExcel Func SearchCourse($_sSearchResult, $_userName) If Not WinExists($hWnd) Then MsgBox(16, "Window closed", "The Internet Explorer window was closed. Please start over.") _Exit() Else _IENavigate($oIE, $urlBBCourseSearch) _IELoadWait($oIE) Local $oForm = _IEGetObjByName($oIE, "courseManagerFormSearch") Local $oSearchString = _IEFormElementGetObjByName($oForm, "courseInfoSearchText") _IEFormElementSetValue($oSearchString, $_sSearchResult) _IEFormSubmit($oForm) _IELoadWait($oIE) Local $oBBTable = _IETableGetCollection($oIE, 2) $aBBTableData = _IETableWriteToArray($oBBTable) Return SearchResult() EndIf EndFunc ;==>SearchCourse Func SearchResult() If Not WinExists($hWnd) Then MsgBox(16, "Window closed", "The Internet Explorer window was closed. Please start over.") _Exit() Else Local $sResult $iSearchIndex = _ArraySearch($aBBTableData, "Course ID", 0, 0, 0, 1, 1, 0) If $iSearchIndex = -1 Then $sResult = "Course Not Found" _Excel_RangeWrite($oWorkbook, Default, $aSearchResult, "C1") Else EnterCourse() $sResult = "UnEnrolled!" _Excel_RangeWrite($oWorkbook, Default, $aSearchResult, "C1") EndIf Return $sResult EndIf EndFunc ;==>SearchResult Func EnterCourse() If Not WinExists($hWnd) Then MsgBox(16, "Window closed", "The Internet Explorer window was closed. Please start over.") _Exit() Else Local $clickFail = "DATE CREATED" _IELinkClickByIndex($oIE, 34) _IELoadWait($oIE) $sourceCode = _IEBodyReadHTML($oIE) If StringInStr($sourceCode, $clickFail) <> 0 Then _IELinkClickByIndex($oIE, 35) _IELoadWait($oIE) EndIf $oLink = _IEGetObjById($oIE, "controlpanel.users.and.groups_groupExpanderLink") _IEAction($oLink, "click") Sleep(500) _IELinkClickByText($oIE, "Users") _IELoadWait($oIE) $aSearchResult[$i] = UnenrollNow($aSearchItems[$i][0], $aSearchItems[$i][1]) EndIf EndFunc ;==>EnterCourse Func UnenrollNow($_sourceCourseId, $_userName) If Not WinExists($hWnd) Then MsgBox(16, "Window closed", "The Internet Explorer window was closed. Please start over.") _Exit() Else Local $UserError = "No users found" Local $sResult $criteriaUsername = _IEGetObjById($oIE, "userInfoSearchKeyString") _IEFormElementOptionSelect($criteriaUsername, 0, 1, 'byIndex') $criteriaUsername = _IEGetObjById($oIE, "userInfoSearchOperatorString") _IEFormElementOptionSelect($criteriaUsername, 0, 1, 'byIndex') ;Paste whats copied from column B into the Username text box. Local $oForm = _IEGetObjByName($oIE, "userManagerSearchForm") Local $oSearchString = _IEFormElementGetObjByName($oForm, "userInfoSearchText") ;PROBLEM HERE... _IEFormElementSetValue($oSearchString, $_userName) ;^^^^^^ WHY IS $_userName ALWAYS B1 ??? Sleep(1000) _IEFormSubmit($oForm) _IELoadWait($oIE) $sourceCode = _IEBodyReadHTML($oIE) If StringInStr($sourceCode, $UserError) <> 0 Then $sResult = "User Not Found" _Excel_RangeWrite($oWorkbook, Default, $aSearchResult, "C1") Return $sResult Else MsgBox(0, "Unenrollment READY!", "We're ready to unenroll foreal") $sResult = "DUN" _Excel_RangeWrite($oWorkbook, Default, $aSearchResult, "C1") Return $sResult EndIf EndIf EndFunc ;==>UnenrollNow Please let me know if any further information is needed.
      If you see other problems or redundancies in my code, please let me know.
      Thank you!
       
    • By SlackerAl
      I have an issue when starting Excel with the following code
      #include <Excel.au3> #include <GUIConstantsEx.au3> Opt("GUIOnEventMode", 1) GUICreate("Excel Test", 600, 440) GUISetOnEvent($GUI_EVENT_CLOSE, "MenuExit") GUISetState(@SW_SHOW) ; Create application object Local $oExcel = _Excel_Open() If @error Then Exit MsgBox(0, "Excel UDF: _Excel_RangeFind Example", "Error creating the Excel application object." & @CRLF & "@error = " & @error & ", @extended = " & @extended) ; sit here forever with an option to react every 10ms While 1 Sleep(10) WEnd Exit Func MenuExit() GUIDelete() Exit EndFunc If the Excel is a standard install, everything is OK. If Excel has the Kutools add-in (https://www.extendoffice.com/product/kutools-for-excel.html) installed and active the excel process runs (and is visible in task manager until killed), but it never displays. Disabling the add-in restores normal functionality.
      If I add some additional code to interact with the excel application then still nothing happens if the add-in is active. However, if Excel is started first and then the AutoIt code is run, it is able to interact with the Excel session as normal.
      Summary: The Excel add-in Kutools prevents excel being started with the _Excel_Open() command from AutoIt. Any AutoIt side work-arounds for this?
×
×
  • Create New...