QPixmap / QImage effizient mit 8 bit Werten füllen

Alles rund um die Programmierung mit Qt
Antworten
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

QPixmap / QImage effizient mit 8 bit Werten füllen

Beitrag von bobcat »

Folgende Aufgabe möchte ich lösen:
Ich habe ein Graustufenbild mit 12 bit Werten, das ich möglichst effizient auf den Schirm bringen will.

Folgendermassen gehe ich vor:
1) 12 bit Graustufen in 8 bit Graustufen umwandeln (ich meine, Qt kann mit 12 bit Werten nicht umgehen, oder?)
2) QImage mit den 8 bit Werten füllen.
3) QPixmap aus dem QImage erstellen.
4) QPixmap in einer QGrapicsView darstellen.

Im Forum hier gibt's einige Threads zum Thema 8bit Bilder darstellen, trotzdem finde ich es sehr kompliziert, das auch hinzubekommen, deshalb nochmal ein neuer Thread.

In meiner Anwendung kommt das Bild von einer Kamera und wird dann aus dem Buffer imgBuffer8, in den die Kamera es schreibt, in ein Array image8 vom Typ uchar kopiert:

Code: Alles auswählen

memcpy(image8, imgBuffer8, imgSize);
Das Ganze passiert in einer Callbackfunktion. Wenn ich nun aus image8 ein QImage erstelle, dann werden die Pixel meine ich noch einmal kopiert, oder? Gibt es eine Möglichkeit, dass sich mein QImage und image8 den Speicher teilen?

Wenn ich nun aus dem QImage ein QPixmap mache, dann werden die Daten nochmal kopiert, oder? Allerdings habe ich es nicht geschafft, die Daten direkt in die QPixmap zu bekommen.

Für einen Hinweis, wie man's richtig macht, wäre ich sehr dankbar. Wie gesagt, es reicht in meiner Anwendung nicht, das irgendwie zu schaffen, sondern es muss noch möglichst effizient sein.
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Beitrag von bobcat »

Habe eben folgenden Thread gefunden und hoffe, dass mich die Infos weiterbringen.

http://www.qtforum.de/forum/viewtopic.p ... adfromdata
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Beitrag von bobcat »

Mit dem Tip aus dem eben zitierten Thread mache ich es nun folgendermassen (image8 enthält die Rohdaten des Bildes):

Code: Alles auswählen

uchar image8;
...
QImage* qImage;
qImage = new QImage(image8, width, height, width, QImage::Format_Indexed8);
QPixmap qPixmap;
QGraphicsScene qScene;

// Rohdaten in das QImage kopieren
memcpy(qImage->bits(), image8, _imgSize);
// QPixmap aus dem QImage erstellen
qPixmap = QPixmap::fromImage(qImage, Qt::AutoColor);
// Pixmap zur QGraphicsScene hinzufügen
qScene.addPixmap(qPixmap);
Wie ich das verstehe, werden bei jeder der drei Aktionen die Daten komplett kopiert. Um das schneller zu gestalten, würde mich folgendes interessieren:

1) Wie bekomme ich die Daten direkt in die Pixmap, ohne den Umweg über das QImage zu gehen? Ich weiss, dass es ein loadFromData(...) gibt. Dieses verlangt aber immer ein format, z.B. bmp, jpg, png, etc. Meine Daten haben aber kein Dateiformat, sondern liegen als 8 bit Rohdaten vor. Verschiedene Threads im Forum behelfen sich damit, einen passenden Dummy Header an die Daten zu hängen und diese dann zu importieren. Das mag funktionieren, scheint mir aber wenig effizient zu sein.

Zudem die Pixmap ja aus dem QImage erstellt werden kann, ohne dass ein format angegeben wurde. QImage hingegen arbeitet mit einem Format, das sich direkter auf die Daten bezieht, wie z.B. das QImage::Format_Indexed8, das ich benutze, das aber bei den Importfunktionen der Pixmap nicht angeboten wird.

Weiß jemand, wie die QPixmap intern aufgebaut ist, wie ich also die Daten z.B. mit memcpy reinbekommen könnte?

2) Wenn ich die QPixmap mit addPixmap(...) zur QGraphicsScene hinzufüge, dann wird meine ich, die Pixmap nochmal kopiert. Kann ich der Scene auch eine Referenz auf die Pixmap mitgeben?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

1. QPixmap benutzt ein platformspezifisches Format - die Daten kann man nicht direkt modifizieren. Deshalb gibt es QImage
2. Die QPixmap werden intern geshared siehe http://doc.qt.nokia.com/4.7/implicit-sharing.html - d.h. es gibt e Ref-counting und erst wenn die Daten modifiziert werden, werden die Daten auch wirklich kopiert. Da sie innerhalb von QGraphicsScene nicht modifiziert werden gibts auch kein memcpy()
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Beitrag von bobcat »

Vielen Dank für die Antwort und den Hinweis auf das implicit sharing in Qt!

Jetzt hätte ich eigentlich nur noch eine offene Frage zum Verständnis: In meiner Anwendung habe ich genau eine Instanz von QImage, in die ich die Daten mit memcpy kopiere. Ebenso habe ich genau eine Instanz von QPixmap. Zwischen den beiden Instanzen gibt es aber kein implicit sharing, das gibt es nur zwischen Pixmaps, oder?

In meiner Anwendung bekomme ich laufend neue Bilder geliefert, die ich auf dem Bildschirm darstellen will. Bei implicit sharing gilt ja copy-on-write, bedeutet das, dass eine Pixmap, die ich zuvor mit

Code: Alles auswählen

qScene.addPixmap(qPixmap);
zu meiner Scene hinzugefügt habe, dann komplett kopiert wird (deep copy), da der eintsprechende Speicher (meine eine Instanz qPixmap) durch das

Code: Alles auswählen

qPixmap = QPixmap::fromImage(qImage, Qt::AutoColor);
überschrieben wurde? Müsste ich also vor jedem addPixmap(...) ein removeItem(...) ausführen? Schliesslich brauche ich die alten Bilder nicht mehr, ich will ja nur das aktuelle darstellen.
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Zum Thema "qScene.addPixmap(qPixmap);" und "update": Eigentlich ist die Idee hinter dem "GraphicsView", dass sich darauf "QGraphicsItem" tummeln, welche (einmal erstell) auch hinterher manipuliert werden können.. damit der Programmierer nicht alle Items selbst erstellen muss, gibt's halt diese Hilfsfunktionen ("addPixmap()"), welche einfach passende Items erstellen.

Daher: nicht laufend ein Item erstellen und löschen, sondern nur einmal erstellen und später wiederverwenden:

Code: Alles auswählen

// einmalig:
item = qScene.addPixmap(qPixmap);

// später nur noch:
item->setPixmap(...);
Zum Thema "Optimierung": ich bin mir nicht so ganz sicher, ob du nicht im Kreis optimierst.. ein "memcpy" auf interne Daten eines QImages IST im Grunde ja ein Deep-Copy (neben dem, welcher Qt je nach dem sowieso schon macht).

Weil sich deine Anforderungen mit grundlegenen OOP-Anforderungen beissen, sind die Default-Items von Qt evt. eh nicht so geeignet für deine Anwendung. Ich würde daher die Sache anderst angehen: mit einem eigenem "QGraphicsItem", welches in der paint(..)-Methode direkt die Rohdaten verwendet. Dann hast du 100% kein einziger Deep-Copy :wink:
Was ich dir nicht sagen kann ist, ob QPainter genügend Performance hat um das zu packen.. aber ein entsprechender Versuch ist ja schnell geschrieben.

hth!
bobcat
Beiträge: 125
Registriert: 21. April 2010 14:51

Beitrag von bobcat »

Sehr gute Tipps, vielen Dank, das werde ich gleich mal ausprobieren!

Ein memcpy brauche ich leider immer, da ich meine Daten einmal aus dem Arbeitsspeicher abholen muss, in den sie meine Kamera schreibt. Idealerweise würde ich sie danach nicht mehr durch die Gegend schaufeln wollen, um sie anzeigen zu können.

Dass Qt hier vielleicht nicht das Mittel der Wahl für meinen Zweck ist, habe ich auch schon überlegt, allerdings bin ich auch noch nicht fündig geworden, was man sonst nehmen könnte. Letztlich muss ich mit den Bildern verschiedene Analysen aus der Bildverarbeitung durchführen und brauche dazu eine GUI, in der der Nutzer mit dem Bild interagieren kann. Bildverarbeitungsbibliotheken in C++, die die Arbeit hinter den Kulissen machen, gibt's viele, bzw. die die Visualisierung übernehmen, z.B. ITK, VTK, OpenGL, OpenVG.

Aber für ein gutes Anzeigewidget starte ich lieber einen neuen Thread: Masken definieren für Bildverarbeitung
solarix
Beiträge: 1133
Registriert: 7. Juni 2007 19:25

Beitrag von solarix »

Ich sagte nicht, dass Qt der falsche Weg ist.. ich habe gesagt, dass die "Default-Items" (also z.B. "QGraphicsPixmapItem") möglicherweise ungeeignet sind und daher ein eigenes Item (abgeleitet von QGraphicsItem) besser auf das Problem passt....!
Antworten