OpenCV & Shared Memory - Bildmatrix kopieren



  • Nabend!

    Ich habe hier ein kleines Problem, das mich langsam in den Wahnsinn treibt.

    Ziel ist es ein Bild in Programm A (Sender) zu laden und dann via "Shared Memory" in Programm B (Receiver) in Empfang zu nehmen und wieder auszugeben.

    Da die OpenCV-Fkt. "cvLoadImage" nur einen Pointer auf das im Speicher liegende Bild zurück gibt und ich auf den gemeinsamen Speicherbereich auch nur via Pointer zugreifen kann, ist mein Plan das Bild mittels "memcpy" in den gemeinsamen Speicherbereich zu heben.

    Das funktioniert leider Alles nicht so ganz...

    Grundsätzlich habe ich erstmal keinen Plan, wie ich die tatsächliche Größe des durch das Bild belegten Speicherbereiches bestimmen kann.
    Das ist ja ziemlich essentiell, da ich a) die Größe für die Reservierung des "SharedMemory" angeben muss und b) für "memcpy" diese Information auch zwingend benötige.

    Bei einem RGB-Bild mit einer Auflösung von 225x225 Pixel und 8Bit Farbtiefe sollte das theoretisch sein:
    3 x 255 x 255 x 1Byte = 151875 Byte

    Kann ich das nicht auch irgendwie zur Laufzeit des Programmes bestimmen lassen?

    Danke für Eure Hilfe!

    MfG
    Aves



  • Deine Rechnung ist richtig wenn die Daten der cv::Mat kontinuierlich im Speicher liegen. Für's Alignment kanns aber schonmal sein, dass du am Ende jeder der Zeilen etwas Padding hast.

    int width = 225;
    int height = 225;
    cv::Mat img( height, width, CV_8UC3 );
    size_t memSize = img.dataend - img.datastar;
    if ( img.isContinuous() )
        ASSERT( memSize == height*width*3 );
    

    Das Beispiel benutzt das C++-Interface, dass es ab OpenCV2 gibt. Dein cvLoadImage kommt allerdings aus dem weniger komfortablen alten C-Interface. Wenn du die Chance hast, das neue zu benutzen, kann ich das nur empfehlen. 🙂

    Wenn du dann in deinem Empfänger deinen shared-Bildspeicher schon hast und ihn wieder in eine cv::Mat bekommen willst, kannst du das übrigens mit den entsprechenden Konstruktoren machen, die nicht selbst allokieren, sondern nen Zeiger nehmen:

    C++: Mat::Mat(int rows, int cols, int type, void* data, size_t step=AUTO_STEP)
    C++: Mat::Mat(Size size, int type, void* data, size_t step=AUTO_STEP)
    C++: Mat::Mat(int ndims, const int* sizes, int type, void* data, const size_t* steps=0)
    

    Siehe hier: http://opencv.itseez.com/modules/core/doc/basic_structures.html#mat-mat
    Aber vielleicht fällt dir ja noch was eleganteres ein, als den nackeligen Speicher rüberzuschieben und den Rest dann rekonstruieren zu müssen. 😉



  • Danke für Tipps!

    Konnte damit ein paar Anpassungen vornehmen. Leider stürzt mein Sender leider mit einem Speicherzugriffsfehler ab, sobald das geladene Bild in den "SharedMemory" kopiert werden soll.
    Ich raffe es nicht, wo der Fehler liegt... Hab das Bild jetzt schwer verkleinert: RGB 128x128 Px.
    Wenn ich das richtig verstehe, kann ein SharedMemory Element max. 128 kByte groß werden, oder wie sehe ich das? Vergl. SHHMAX: [url]http://openbook.galileocomputing.de/linux_unix_programmierung/Kap09-006.htm#Xxx999240 [/url]
    Das Bild das ich nutze, ist aber wesentlich kleiner mit seinen 48 kByte.

    Wenn die Profis mal drüberschauen könnten? Bis Zeile 49 läuft es. Dann steigt das Programm mit einem Speicherzugriffsfehler aus.

    #include <stdlib.h>
    #include <stdio.h>
    #include <cv.h>
    #include <highgui.h>
    #include <sys/types.h>
    #include <sys/ipc.h>
    #include <sys/shm.h>
    #include <iostream>
    #include <unistd.h>
    
    using namespace cv;
    using namespace std;
    
    int main()
    {
    	Mat image;
    	Mat *p_img0;
    	int *sema;
    	int shmid0;
    	int shmid1;
    	key_t key0;
    	key_t key1;
    	size_t memSize;
    
    	//Name der Shared Memory Segmente
    	key0 = 7887;
    	key1 = 8998;
    
    	image = imread("lenna.png");
    	if(!image.data){
    		cout << "Fehler beim Laden des Bildes!" << endl;
    		}
    	cout << "size: " << image.size().height << " , " << image.size().width << endl;
    	memSize = image.dataend - image.datastart;
    	cout << "memSize: " << memSize << endl;
    
    	//Shared Memory Segmente anlegen
    	shmid0 = shmget(key0, memSize, IPC_CREAT | 0666);
    	if(shmid0 < 0){
    		cout << "Anlegen des Speicherbereiches gescheitert!" << endl;
    	}
    	shmid1 = shmget(key1, 10, IPC_CREAT | 0666);
    
    	//Segmente an lokale Ressourcen hängen
    	p_img0 = (Mat*)shmat(shmid0, NULL, 0);
    	cout << "Pointer auf SHM :" << p_img0 << endl;
    	sema = (int*)shmat(shmid1, NULL, 0);
    
    	image.copyTo(*p_img0);
    	*sema = 12345;
    	cout << "*sema :" << *sema << endl;
    
    	namedWindow("Sender");
    	imshow("Sender", *p_img0);
    
    	waitKey(0);
    
    	return 0;
    }
    


  • Update:

    $ cat /proc/sys/kernel/shmmax ergab 32 MByte
    /proc/sys/kernel/shmin exisitiert nicht.

    Da vermute ich mal, das es dann wohl keine Untergrenze gibt. Die Grenzen eines SharedMemory Bereiches auf meinem System sollten also nicht das Problem sein.



  • Seh ich das richtig, dass du so tust, als wäre eine Instanz der Klasse cv::Mat nichts anderes als der Bildspeicher, den sie benutzt? 🙂



  • Ach Mist...

    Alles klar!



  • Ähm, sehe ich das richtig, das ich jetzt für jedes Element der Klasse die ich benutze, einen eigenen SharedMemory Bereich anlege, jedes Element in einen Speicherbereich kopiere.
    Um dann am Ziel das wieder aus den einzelnen Bereichen den entsprechenden Elementen des Objektes wieder zuzuordnen?

    Das aber hässlich...



  • Ja, das wär allerdings hässlich. 😉
    Ich würde zusätzlich zum Bildspeicher nur die Informationen an das andere Programm übergeben, die es braucht. Wenn es bei dir nur um Bilder geht, sollten das Höhe, Breite, Linestep, und eventuell Anzahl der Kanäle und bits pro Kanal sein, jenachdem wie allgemeingültig du es machen willst.
    Die cv::Mat mit placement new direkt in der shared memory zu konstruieren könnte gefährlich werden, weil das (nicht nur wegen Alignment) eventuell scheppert. Ob du das mit POD so hinbekommst, dass es UB ist, weiß ich nicht. Da du ja deine Zielanwendung vermutlich eh irgendwie den namen des shared memory segments mitteilst und was es damit tun soll, kannst du auf dem Weg ja auch die oben genannten Infos mit übermitteln.

    Edit: In der Zielanwendung käm dann übrigens einer der Konstruktoren, die ich im ersten Post erwähnte, dran. 🙂



  • Danke nochmal für deine Hilfe!

    Habe es hinbekommen.


Anmelden zum Antworten