Seite 1 von 1

wie mache ich Grafikfunktionen schnell?

Verfasst: 12. Mai 2008 21:18
von qtNiko
hi,

die Aufgabe besteht darin, eine Sequenz von Bildern jeweils einzulesen, Linien einzumalen und darzustellen. Und das soll möglichst schnell sein - letztendlich unter 15ms pro Schleife. Ein erster Versuch damit war sehr ernüchternd, was die Geschwindigkeit betrifft:

Code: Alles auswählen

...
    bild = new QPixmap(768, 576);
	QPainter painter;  // zum Malen der Linien
	for (il=0; il<ANZ_LOOPS; il++)  {
		bnr = 100 +  (il % 10);  // unabhängig von ANZ_LOOPS werden immer nur 10 Bilder zyklisch dargestellt
		fname = "testbild_"  + QString::number(bnr) + ".pgm";
		bild->load(fname);   // das Bild in der Datei ist nur 256x256 Pixel groß
		painter.begin(bild);

		painter.setPen(Qt::green);
		for (i=0; i<ANZ_SLINES; i++) 
			painter.drawLine(slines[i].anfx+il,slines[i].anfy, slines[i].endx, slines[i].endy);
		
		painter.setPen(Qt::red);
		for (i=0; i<ANZ_MLINES; i++) 
					painter.drawLine(mlines[i].anfx+il, mlines[i].anfy, 	 mlines[i].endx, mlines[i].endy);
		
		painter.end();
		ZeichenBox->setPixmap(*bild);  //  ZeichenBox ist ein QLabel zur Darstellung
		QApplication::processEvents();  // damit alle Bilder zu sehen sind
	}
1. Auf meinem Athlon 3200+ dauert ein Schleifendurchlauf über 100ms! Das ist um eine Größenordnung zu lange! Kann man das nicht schneller machen, und wie?

2. Ich lese ein 256x256 großes Bild in ein 768x576 großes Pixmap und würde erwarten, dass nur ein kleines Bild links oben zu sehen ist. Tatsächlich aber füllt das eingelesene Bild die ganze Fläche aus. Wie kann ich erreichen, dass das eingelesene Bild genau so groß dargestellt wird, wie es ist?

Mein System: HP Pavillon 3237, Athlon64 3200+ , GeForce 6200, Suse Linux 10.1, Eclipse-Europa

Verfasst: 12. Mai 2008 22:01
von upsala
1. Du lädst ein Bild (wahrscheinlich immer das selbe?). Das muß von der Platte gelesen und decodiert werden.

Du führst processEvents aus, damit wird aber nicht nur das Label neu gezeichnet.

Wieviele Linien zeichnest du, könnte man das cachen? In einem QPicture oder einem QPixmap?


2. Load & Copy

Verfasst: 12. Mai 2008 22:16
von qtNiko
1. Du lädst ein Bild (wahrscheinlich immer das selbe?). Das muß von der Platte gelesen und decodiert werden.
Auf der Platte steht das Bild im pgm-Format, binär. In meiner endgültigen Applikation wird es über den PCI-Bus eingelesen, was sicher schneller sein wird. Aber das Einlesen des Bildes gehört in der Schleife auf jeden Fall dazu.
Du führst processEvents aus, damit wird aber nicht nur das Label neu gezeichnet.
Wie kann ich sonst Qt dazu bringen, alle Bilder anzuzeigen, nicht nur das letzte?
Wieviele Linien zeichnest du, könnte man das cachen? In einem QPicture oder einem QPixmap?
In meinem Test sind es 230 Linien, die künftige Maximalzahl.
Wie sieht das aus, cachen? Ich male die Linien bereits auf ein QPixmap, und zwar in der Reihenfolge zuerst das Bild in das QPixmap und danach die Linien.
Wäre die Verwendung von QPicture schneller?
2. Load & Copy
Das ist mir leider zu stenografisch. Also, mit load() lade ich doch das Bild von Datei in das QPixmap. Was soll ich dann noch mit Copy?

Verfasst: 12. Mai 2008 22:26
von upsala
Du wolltest eine Begründung, warum deine Schleifen so langsam sind, ich hab dir ein paar mögliche Gründe genannt.

Und bei 15ms Wiederholrate stellt man nicht alle Bilder dar, schließlich ist es für das menschliche Auge nicht mehr erfaßbar => unnötiger Zeitverbrauch

Du hast meine Frage, bezüglich des cachen nicht beantwortet. Zeichnest du immer die selben Linien?

Ich kann jetzt auch den Grund nicht nachvollziehen, warum das Bild bei dir beim laden skaliert wird. Also in ein Separates QPixmap laden und dann ins Zielbild kopieren. Wäre ein Möglichkeit.

Verfasst: 12. Mai 2008 22:41
von qtNiko
Du hast meine Frage, bezüglich des cachen nicht beantwortet. Zeichnest du immer die selben Linien?
In meinem Testbeispiel sind es immer die gleichen Linien. Später aber werden es jeweils unterschiedliche und auch unterschiedlich viele sein. Damit ich sehe, ob jedesmal die Linien neu gemalt werden, lasse ich die x-Anfangskoordinate pixelweise laufen.

Und bei 15ms Wiederholrate stellt man nicht alle Bilder dar, schließlich ist es für das menschliche Auge nicht mehr erfaßbar => unnötiger Zeitverbrauch
Na ja, das Auge soll eine flüssige Bewegung sehen. Die 15 ms habe ich mal so abgeschätzt, weil später noch einiges dazukommen wird, und alles dann mit 50Hz ablaufen muss.

Ich werde noch weiter testen, aber ich vermute, dass das Malen der Linien den größten Teil der Zeit braucht. Qt hat doch auch OpenGL-Funktionen. Ich kenne sie nicht, aber wenn ich OpenGL höre, dann denke ich an Grafikkartenunterstützung. Damit müsste es doch wesentlich schneller werden, oder?

Verfasst: 12. Mai 2008 22:58
von Christian81
50Hz mit einem QImage-decode + Scale + Anzeige auf ein QWidget in der Größe von 256x256? Imho unmöglich.
2. Ich lese ein 256x256 großes Bild in ein 768x576 großes Pixmap
aha - ich würde sagen wir fangen nochmal von vorn an und schauen was wir programmiert haben.

Verfasst: 12. Mai 2008 23:12
von qtNiko
aha - ich würde sagen wir fangen nochmal von vorn an und schauen was wir programmiert haben.
In meinem Originalposting steht der Code (hier noch mal verkürzt auf das Wesentliche):

Code: Alles auswählen

bild = new QPixmap(768, 576);
   QPainter painter;  // zum Malen der Linien
   for (il=0; il<ANZ_LOOPS; il++)  {
      ...
      fname = ...;
      bild->load(fname);   // das Bild in der Datei ist nur 256x256 Pixel groß 
...     Linien malen mit drawLine(...)
...
ZeichenBox->setPixmap(*bild);  //  ZeichenBox ist ein QLabel zur Darstellung
      QApplication::processEvents();  // damit alle Bilder zu sehen sind 
}
Das müsste doch so klappen?
Das Skalieren passiert vermutlich am Ende bei ZeichenBox->setPixmap(*bild)?
Das mag ja in vielen Fällen praktisch sein, wenn man nicht extra skalieren muss, um das Bild stets gefüllt zu bekommen. Aber ich bräuchte es gerade ohne Skalierung :?

Jetzt habe ich ein paar interessante Zeitmessungen:
Wenn man den Job aufteilt in a) Bild von Datei lesen, b) 230 Linien einmalen und das QPixmap an das QLabel übergeben, c) Bild darstellen mit QApplication::processEvents(), dann ergibt sich folgendes pro Schleifendurchlauf:
a+b+c: 170ms
b+c: 160ms
a+b: 30ms
b: 3ms

Das Darstellen per QApplication::processEvents() (mit allem, was sonst noch dabei passiert) frisst fast die gesamte Zeit.

Wie kann ich jedes einzelne Bild darstellen, ohne jedoch QApplication::processEvents() zu verwenden?

Verfasst: 13. Mai 2008 08:04
von PeterLustig
Sie die Bilder die du lädst denn immer dieselben, oder verändern sich diese? Wenn sie die gleichen sind, musst du sie ja nicht jeden Schleifendurchlauf neu laden.

Verfasst: 13. Mai 2008 08:08
von Christian81
Unabhängig von der Geschwindigkeit - Du erzeugst ein Biuld mit 768*512 und dann lädst Du ein anderes - das meine ich.

Verfasst: 13. Mai 2008 09:13
von upsala
Dein Problem ist nicht, daß du die Grafiken nicht schnell genug darstellen kannst, sondern daß du eine Framerate willst, die nicht sinnvoll ist.

Außerdem solltest du uns mal mitteilen, was an deiner Grafik statisch und was dynamisch ist.

Verfasst: 13. Mai 2008 09:31
von qtNiko
Jetzt verstehe ich: Mit "bild.load(fname)" wird das QPixmap ganz neu definiert, d.h. auch in den Abmessungen. Ich dachte, mit load() wird nur in ein bestehendes QPixmap Inhalt reingeladen. So etwas brauche ich nämlich letztendlich, dass in ein 768x576 großes Bild links oben ein kleineres Bild eingesetzt wird. Dann bleibt natürlich rechts und unten ein Rand, der dann mit alphanumerischen Daten gefüllt wird, siehe das Bildbeispiel im Anhang.
Dazu muss ich mir erst mal zusammensuchen, was es dafür gibt - hat jemand einen schnellen Tipp?

Verfasst: 13. Mai 2008 10:15
von Christian81
QPainter::drawPixmap() - aber das macht das ganze noch langsamer.
Oder gleich auf das QLabal (oder was auch immer Du benutzt) zeichnen, ggf. QGLWidget benutzen damit OpenGL benutzt wird.
D.h. einen QTimer mit 50Hz starten und dort ein Q(GL)Widget::update() auf das zu zeichnende Widget auslösen.

Verfasst: 13. Mai 2008 10:42
von qtNiko
ok, danke jetzt habe ich eine Spur. Der Rest ist dann hoffentlich nur noch Fleißarbeit :)