OpenCV meets AutoIt

  • Hallo Leute,

    ich hab mich mal zu gerungen eine UDF zumachen um OpenCV in AutoIt verwenden zu können. Da ich einige Probleme wahrscheinlich haben werde, wird dieser Thread wahrscheinlich lang werden. Und wenn dann die UDF fertig ist, werd ich ihn dann auf gelöst setzen ( autoBert ;)).

    Zum ersten Problem:
    Ich bin grad dabei die Funktion cvCircle umzusetzen.
    http://www.emgu.com/wiki/files/1.5…b7ad03cefae.htm
    In AutoIt bin ich nun schon soweit:

    Spoiler anzeigen
    [autoit]

    Global Const $tagCvPoint = _
    "int x;" & _
    "int y"

    [/autoit] [autoit][/autoit] [autoit]

    Global Const $tagCvScalar = "double val[4]"

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    _OpenCV_Circle($iImage, 50, 100, 20)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Func _OpenCV_Circle($_iImage, $_iX, $_iY, $_iRadius, $_iThickness = 1, $_iLineType = 8, $_iShift = 0)
    Local $_tCenter = DllStructCreate($tagCvPoint)
    If @error Then Return SetError(1, @error, 0)
    Local $_tColor = DllStructCreate($tagCvScalar)
    If @error Then Return SetError(2, @error, 0)
    DllStructSetData($_tCenter, "x", $_iX)
    DllStructSetData($_tCenter, "y", $_iY)
    DllStructSetData($_tColor, "val", 150, 1)
    DllStructSetData($_tColor, "val", 150, 2)
    DllStructSetData($_tColor, "val", 150, 3)
    DllStructSetData($_tColor, "val", 0, 4)
    Local $_aResult = DllCall($__ghOCVDll_1, "int:cdecl", "cvCircle", "int", $_iImage, "ptr", DllStructGetPtr($_tCenter), "int", $_iRadius, _
    "ptr", DllStructGetPtr($_tColor), "int", $_iThickness, "int", $_iLineType, "int", $_iShift)
    If @error Then Return SetError(3, @error, 0)
    Return $_aResult[0]
    EndFunc

    [/autoit]


    Nun bekomme ich aber eine Fehlermeldung von der Dll.

    Code
    OpenCV Error: Assertion failed (radius >= 0 && thickness <= 255 && 0 <= shift && shift <= XY_SHIFT) in unknown function, file ..\..\..\src\opencv\modules\core\src\drawing.cpp, line 1628


    Die dazugehörige Funktion in der Cpp sieht so aus:

    Spoiler anzeigen

    Weiß jemand vielleicht wo der Fehler liegt?

  • Hi,
    die Fehlermeldung kommt sicherlich nicht von AutoIt!
    Also entweder das komplette Script posten, so dass man es auch ausprobieren kann, oder selber weitertüfteln...was steht denn in der line 1628 aus der Fehlermeldung?

    Weiterhin ist der Typ von $tagCvScalar double, allerdings übergibst du mit 150 einen INT, um double zu verwenden, nimm 150.0

  • Ich hab doch auch geschrieben, dass die Meldung von der Dll kommt.
    Die Line wäre

    Code
    CV_Assert( radius >= 0 && thickness <= 255 &&
            0 <= shift && shift <= XY_SHIFT );


    Aber du kannst ja selber mal testen.

    Also es soll so laufen. Er wandelt ein Bild in Graustufen und zeichnet einen Kreis drauf un speichert es ab.
    Wenn du _OpenCV_Circle auskommentierst siehst du das.

  • Hi,
    hast du selbst compiliert?

    Dann kommentiere mal die CV_Assert aus, bzw. was macht diese Funktion?

    Hab mal cvLine() ausprobiert, derselbe Fehler!

    Spoiler anzeigen
    [autoit]

    #include "OpenCV.au3"

    [/autoit] [autoit][/autoit] [autoit]

    #include "array.au3"

    [/autoit] [autoit][/autoit] [autoit]

    Global $iImage, $iWidth

    [/autoit] [autoit][/autoit] [autoit]

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

    [/autoit] [autoit][/autoit] [autoit]

    $a = _OpenCV_Startup()
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $a = ' & $a & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    ;Exit
    $iImage = _OpenCV_LoadImage("Test.jpg") ;, $CV_LOAD_IMAGE_GRAYSCALE)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iImage = ' & $iImage & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit]

    $struct = "int nSize;" & _
    "int ID;" & _
    "int nChannels;" & _
    "int alphaChannel;" & _
    "int depth;" & _
    "char colorModel[4];" & _
    "char channelSeq[4];" & _
    "int dataOrder;" & _
    "int origin;" & _
    "int align;" & _
    "int width;" & _
    "int height;" & _
    "ptr _IplROI ;" & _
    "ptr _IplImage ;" & _
    "ptr imageId;" & _
    "ptr _IplTileInfo ;" & _
    "int imageSize;" & _
    "ptr imageData;" & _
    "int widthStep;" & _
    "int BorderMode[4];" & _
    "int BorderConst[4];" & _
    "ptr imageDataOrigin"

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    $iplimage = DllStructCreate($struct, $iImage)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $iplimage = ' & $iplimage & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit]

    $width = DllStructGetData($iplimage, "width")
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $width = ' & $width & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit]

    ;exit

    [/autoit] [autoit][/autoit] [autoit]

    ; $b=_OpenCV_Circle($iImage, 50, 100, 20)
    ;~ ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $b= = ' & $b & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console
    ;~ If @error Then MsgBox(0, "_OpenCV_Circle", @error & "-" & @extended)

    [/autoit] [autoit][/autoit] [autoit]

    $_ix1=10
    $_iY1=10

    [/autoit] [autoit][/autoit] [autoit]

    $_iX2=50
    $_iY2=50
    Local $_tCenter1 = DllStructCreate($tagCvPoint)
    DllStructSetData($_tCenter1, "x", $_iX1)
    DllStructSetData($_tCenter1, "y", $_iY1)
    Local $_tCenter2 = DllStructCreate($tagCvPoint)
    DllStructSetData($_tCenter2, "x", $_iX2)
    DllStructSetData($_tCenter2, "y", $_iY2)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Local $_tColor = DllStructCreate($tagCvScalar)

    [/autoit] [autoit][/autoit] [autoit]

    DllStructSetData($_tColor, "val", 150.0, 1)
    DllStructSetData($_tColor, "val", 150.0, 2)
    DllStructSetData($_tColor, "val", 150.0, 3)
    DllStructSetData($_tColor, "val", 0.0, 4)

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    Local $_aResult = DllCall($__ghOCVDll_1, "int:cdecl", "cvLine", "int", $iImage, "ptr", DllStructGetPtr($_tCenter1), "ptr", DllStructGetPtr($_tCenter2), _
    "ptr", DllStructGetPtr($_tColor), "int", 1, "int", 8, "int", 0)

    [/autoit] [autoit][/autoit] [autoit]

    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $_aResult = ' & $_aResult & @crlf & '>Error code: ' & @error & @crlf) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit]

    _ArrayDisplay($_aResult)

    [/autoit] [autoit][/autoit] [autoit]

    $d = _OpenCV_NamedWindow("Test")
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $d = ' & $d & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console
    $e = _OpenCV_ShowImage("Test", $iImage)
    ConsoleWrite('@@ Debug(' & @ScriptLineNumber & ') : $e= = ' & $e & @CRLF & '>Error code: ' & @error & @CRLF) ;### Debug Console

    [/autoit] [autoit][/autoit] [autoit]

    Sleep(2000)
    _Exit()

    [/autoit] [autoit][/autoit] [autoit]

    Func _Exit()
    _OpenCV_SaveImage(@DesktopDir & "\test1234.jpg", $iImage)
    _OpenCV_ReleaseImage($iImage)
    _OpenCV_Shutdown()
    MsgBox(0, "", "Fertig")
    Exit
    EndFunc ;==>_Exit

    [/autoit] [autoit][/autoit] [autoit][/autoit]
    Code
    OpenCV Error: Assertion failed (0 <= shift && shift <= XY_SHIFT) in unknown function, file ..\..\..\src\opencv\modules\core\src\drawing.cpp, line 1574


    Da wird irgendeine unbekannte Funktion in CV_Assert() aufgerufen...

  • ne ich habs nicht selber kompiliert.

    Aber danke schonmal für das IplImage. Habs nicht hinbekommen die Struktur zu übersetzen ins AutoIt-konforme.
    Kann jetzt an einer anderen Stelle weitermachen :D.

  • So das Zeichnen las ich jetzt erstmal, bis ich einen Gedankenblitz hab und mir die Lösung spontan einfällt (hoffe ich :S ).

    Das Nächste wäre dann dies hier.

    Code
    vector<int> compression_params;
    compression_params.push_back(CV_IMWRITE_PNG_COMPRESSION);
    compression_params.push_back(9);


    Das ist C++.

    Wie sieht das in AutoIt aus?
    Ist das ein Array?

  • Das ist eine Klasse aus der C++ Standard Template Library.
    Das in AutoIt zu überführen stelle ich mir sehr finster vor...
    Wenn du OpenCV wrappen willst dann halte dich am besten an die C-Schnittstellen anstatt an die für C++ denn dann solltest du fast alles über DLLStruct hinbekommen.
    Dennoch ein enormer Aufwand den du dir da vorgenommen hast.

  • Also Bilder laden und speichern funktioniert schonmal. Sowie Bilder von der Webcam holen.
    Bin grad dabei die Laplace-Funktion umzusetzen. Hätte nicht gedacht, dass das alles so schwierig ist. Aber ich bleib mit Sicherheit dran.

    @AJ: Leider sind die meisten für C++ ausgelegt, aber nach und nach wird es was. Ich hab gelesen das vector<int> nicht anderes als ein Array aus Integern ist.

    Was ich seltsam finde. Wenn ich beim DllCall die Variable übergebe als Integer, bekomme ich öfters die Fehlermeldung von OpenCV, dass es kein Int ist. ?(
    Aber das knack ich auch noch.

  • @AJ: Leider sind die meisten für C++ ausgelegt, aber nach und nach wird es was.

    Welche denn?
    Die C++ Funktionen von OpenCV sind in der Regel nichts weiter als Wrapper-Funktionen für die C-Funktionen.
    Gerade für dein letztes Beispiel mit compression_params bei imwrite.
    Warum quälst du dich mit dem STL-Vector rum wenn du bei der entsprechenden C-Funktion cvSaveImage gleich ein simples Integer-Array verwenden kannst?

    Ich hab gelesen das vector<int> nicht anderes als ein Array aus Integern ist.

    Das ist prinzipiell richtig. Intern werden die Daten nicht anders als in einem C-Array gespeichert. Dennoch steckt da herum noch ein Objekt mit einigen anderen Eigenschaften. Daher kann man es nicht direkt per DllCall wie ein C-Array ansprechen.

  • Ich verwende ja cvSaveImage und das Array übergebe ich als DllStruct.

    Aber irgendwie ändert sich bei dem Bild nix.

  • Einen von meinem Problemen ist auch, dass einige Funktionen ein Struct zurückgeben pur, also keinen Pointer. Z.B. die Funktion cvGetImageROI. Die gibt ein Rect-Struct zurück. Aber DllCall unterstützt es ja bei den Rückgabetypen nicht. Wie könnte man das lösen?
    Hab mal eine Zip hochgeladen um es mal selber zu testen. Nicht meckern, die UDF ist ja noch in der Entwicklungsphase ;)

    https://www.box.com/s/0d234ac36fb8245ad481

  • Die Methode kenne ich. Aber das unterstützt ja nicht die Dll-Funktion. Ich hätte es auch so gemacht, da ich es auch so kenne. Aber nicht das eine Funktion ein Struct zurückliefert. Was du meinst, ist eine Art ByRef. Ich meine aber den Rückgabewert.

  • So Laplace und Sobel funktionieren.

    Hänge nun an dieser Struktur:


    Wie sieht die in AutoIt aus? Das Union weiß ich nicht umzusetzen. Mit den Sternchen ist klar, die werden einfach zu "ptr".


  • Das ist prinzipiell richtig. Intern werden die Daten nicht anders als in einem C-Array gespeichert. Dennoch steckt da herum noch ein Objekt mit einigen anderen Eigenschaften. Daher kann man es nicht direkt per DllCall wie ein C-Array ansprechen.


    Ein vector ist eine verkettet Liste und keine C-Array. Jedes Element speichert einen Pointer zum nächsten und vorherigen und natürlich die Daten. Man hat also eine zusätzliche Datenlast von 8 Byte pro Element im Array (16 Byte unter 64 Bit)

  • Sicher das du das nicht mit std::list<T> verwechselst?
    Zitat aus der C++-Reference:

    Zitat

    It is implemented as an contiguous array, which means that pointer arithmetic is fully supported, and a pointer to an element of a vector may be passed to any C function that expects a pointer to an element of an array.


    Wenn die Vector-Struktur tatsächlich als doppelt verlinkte Liste aufgebaut wäre könnte man flott und einfach auch Elemente mittendrin einfügen und löschen was man bei std::vector ja allerdings nicht machen sollte.
    Außerdem wäre std::list dann überflüssig.

    Das Union weiß ich nicht umzusetzen.

    Bei Union stehen alle Elemente an der selben Stelle im Speicher.
    Also kann man immer nur ein Element davon wirklich verwenden - je nachdem als welchen Datentyp man die Speicherstelle anspricht.
    In der Struct sollte also für die Union soviel Platz reserviert wie für den größten Datentyp in der Union.
    Spontan würde ich also in der Struct für die Union ein Byte-Array entsprechender Größe nehmen und beim Auslesen und Schreiben da auf den Pointer je nach Datentyp entsprechend handeln.
    Hier dient die Union als Template-Ersatz so dass mehrere verschiedene Datentypen verwendet werden können.

    2 Mal editiert, zuletzt von AspirinJunkie (16. August 2012 um 09:38)

  • Jetzt haste mich ;)
    Gemacht hab ich sowas noch nicht - das waren erstmal nur meine theoretischen Überlegungen hierzu.

    Praktisch umgesetzt stelle ich es mir so vor:

    Union in Struct
    [autoit]

    $tagCvMat = DllStructCreate("int type;" & _
    "int step;" & _
    "INT_PTR refcount;" & _
    "int hdr_refcount;" & _
    "ptr data;" & _ ; Das ist für die Union: Ein Pointer ist der größte Datentyp innerhalb der Union
    "int rows;" & _
    "int cols;")

    [/autoit] [autoit][/autoit] [autoit][/autoit] [autoit]

    ; Schreiben von Union als double:
    $tagDataAsFloat = DllStructCreate("double")
    DllStructSetData($tagDataAsFloat, 1, 12.6)
    DllStructSetData($tagCvMat, "data", DllStructGetPtr($tagDataAsFloat))

    [/autoit] [autoit][/autoit] [autoit]

    ; Lesen von Union als double:
    $tagReadDataAsFloat = DllStructCreate("double", DllStructGetData($tagCvMat, "data"))
    MsgBox(0, "", "Inhalt: " & DllStructGetData($tagReadDataAsFloat, 1))

    [/autoit] [autoit][/autoit] [autoit]

    ; Verändern vom Inhalt (kann erst so ausgeführt werden wenn data mindestens einmal einen Wert bekommen hat - sonst zeigt der Pointer noch auf 0 )
    $tagDataAsFloat = DllStructCreate("double", DllStructGetData($tagCvMat, "data"))
    DllStructSetData($tagDataAsFloat, 1, 5.7)
    DllStructSetData($tagCvMat, "data", DllStructGetPtr($tagDataAsFloat))

    [/autoit] [autoit][/autoit] [autoit]

    ; erneutes Lesen von Union als double:
    $tagReadDataAsFloat = DllStructCreate("double", DllStructGetData($tagCvMat, "data"))
    MsgBox(0, "", "Neuer Inhalt an der selben Position im Speicher: " & DllStructGetData($tagReadDataAsFloat, 1))

    [/autoit]


    In dem Fall von CvMat gibt der Pointer data natürlich aber nicht einen Pointer auf eine einzelne Variable an sondern auf ein komplettes Array.

    Da data ja später mal auf ein Array zeigen soll hier nochmal die Variante für ein double-Array auf data:

    Union in Struct mit Array
    [autoit]

    $tagCvMat = DllStructCreate("int type;" & _
    "int step;" & _
    "INT_PTR refcount;" & _
    "int hdr_refcount;" & _
    "ptr data;" & _ ; Das ist für die Union: Ein Pointer ist der größte Datentyp innerhalb der Union
    "int rows;" & _
    "int cols;")

    [/autoit] [autoit][/autoit] [autoit]

    ; Schreiben von Union als double:
    $tagDataAsFloat = DllStructCreate("double[3]")
    DllStructSetData($tagDataAsFloat, 1, 1.2, 1)
    DllStructSetData($tagDataAsFloat, 1, 2.3, 2)
    DllStructSetData($tagDataAsFloat, 1, 3.4, 3)
    DllStructSetData($tagCvMat, "data", DllStructGetPtr($tagDataAsFloat))

    [/autoit] [autoit][/autoit] [autoit]

    ; Lesen von Union als double:
    $tagReadDataAsFloat = DllStructCreate("double[3]", DllStructGetData($tagCvMat, "data"))
    MsgBox(0, "", "Inhalt von data[1]: " & DllStructGetData($tagReadDataAsFloat, 1, 2))

    [/autoit] [autoit][/autoit] [autoit]

    ; Verändern vom Inhalt (kann erst so ausgeführt werden wenn data mindestens einmal einen Wert bekommen hat - sonst zeigt der Pointer noch auf 0 )
    $tagDataAsFloat = DllStructCreate("double[3]", DllStructGetData($tagCvMat, "data"))
    DllStructSetData($tagDataAsFloat, 1, 5.7, 2)
    DllStructSetData($tagCvMat, "data", DllStructGetPtr($tagDataAsFloat))

    [/autoit] [autoit][/autoit] [autoit]

    ; erneutes Lesen von Union als double:
    $tagReadDataAsFloat = DllStructCreate("double[3]", DllStructGetData($tagCvMat, "data"))
    MsgBox(0, "", "Neuer Inhalt von data[1]: " & DllStructGetData($tagReadDataAsFloat, 1, 2))

    [/autoit]

    Einmal editiert, zuletzt von AspirinJunkie (16. August 2012 um 09:45)