2012-09-28 14 views
18

Ho progettato un programma che, in pratica, taglia una forma geometrica in molti piccoli triangoli (in una "tela sinistra"), applica una semplice trasformazione matematica al mazzo di triangoli e li ridisegna in la loro nuova configurazione. Guarda la schermata qui sotto.Qt/C++: disegno efficiente

screen cap 1

Al fine di richiamare questi triangoli, io uso QPainter::drawPolygon. Ogni triangolo a destra corrisponde a un triangolo a sinistra, quindi so quale colore voglio usare per disegnarlo.

Finora, bene. Anche se disegno molti più triangoli di questo (quando uso triangoli molto più piccoli per tagliare la forma), questo è abbastanza veloce.

Ho aggiunto una funzione al mio programma: posso disegnare triangoli estratti da un'immagine invece di triangoli semplici: vedere la seguente schermata di cattura.

enter image description here

Il problema è che il mio modo di fare questo è troppo lento. Ecco come lo faccio:

  1. corro attraverso tutti i triangoli
  2. Per ogni triangolo, io calcolare le coordinate di ogni pixel che verrà visualizzato.
  3. Per ognuno di questi pixel, computo le coordinate del pixel corrispondente sull'immagine (questa è un'operazione matematica semplice) e recupero il colore di quel pixel.
  4. Io uso QPainter::setPen(QColor) e QPainter::drawPoint(QPoint) per disegnare il pixel.

Sono nuovo alla programmazione in Qt e non so nulla di grafica, quindi questo è quello che potrei inventare. Il problema è che è "inaccettabilmente" troppo lento (lo paintEvent di ogni tela richiede circa 0,15 s, rispetto a 0,01 nel caso di triangoli semplici).

ho eseguito un profiler per cercare di capire cosa sta succedendo, ho notato che in viene speso del paintEvent,

  1. 58% del tempo è trascorso in QPainter::drawPoint
  2. il 27% del tempo widget di tela in QPainter::setPen

sembra che QPainter::drawPoint è troppo complicato e lento: voglio solo per stampare un pixel di un determinato colore, il gioco è fatto.

Probabilmente ho trovato una soluzione al mio problema: archiviare un QImage (come variabile membro del mio widget tela) che rappresenta l'intera cosa che desidero visualizzare sul mio canvas e definirlo interamente nel mio paintEvent pixel per pixel, e quindi disegnarlo immediatamente alla fine del mio paintEvent con QPainter::drawImage. Ho un suggerimento che questo sarà molto più veloce. Ma prima di riscrivere il mio codice ancora una volta, mi piacerebbe sapere se è davvero quello che voglio fare.

Spero di non averti annoiato a morte! Molte grazie in anticipo per i tuoi approfondimenti.

+1

stai disegnando pixel per pixel ?? (zomg !!) – UmNyobe

risposta

4

non OpenGl soluzione:

utilizzare un buffer RGB per l'immagine di destinazione. Lavora attraverso i tuoi primi 3 passi come hai fatto prima. Una volta individuata la posizione e il colore del pixel, lo si imposta su questo buffer. Quindi si utilizza

QImage::QImage (uchar * data, int width, int height, Format format) 

per costruire l'immagine in base al buffer precedente. È vicino alla soluzione che hai fornito e sarà molto più veloce di quello che hai attualmente.

+0

Mille grazie!Probabilmente userò questo (fino a quando non imparerò OpenGL un giorno). – Seub

+2

Confermo, questo va bene per quello che faccio! (paintEvent in max 0.04s) – Seub

7

OpenGL fa molto bene la mappatura delle coordinate di immagine (trama). Probabilmente vuoi usare qualche forma di OpenGL. Qt ha qualche legame con OpenGL che può aiutarti.

+0

+1 D'accordo, sembra che l'OP stia creando un'applicazione di mappatura UV, OpenGL è lo strumento perfetto per questo. – cmannett85

+0

Grazie per la risposta! Ne esaminerò sicuramente un giorno. Il problema è che non so nulla di OpenGL. – Seub

3

Un modo per farlo sarebbe utilizzare una classe che eredita da QGLWidget invece della combinazione QGraphicsScene/QGraphicsView. Sfortunatamente, la curva di apprendimento per OpenGL inizia un po 'in salita. Tuttavia, sarà molto veloce perché avverrà direttamente sulla scheda grafica che è ottimizzata per questo tipo di operazione.
Caricherai l'immagine QGLWidget::bindTexture().
Potrai associare i punti nell'immagine con la mesh triangolare e inviarli tutti alla tua scheda grafica. Nella versione precedente di OpenGL (che è più facile da usare rispetto alla API di recente a mio parere), sarebbe simile a questa:

glEnable(GL_TEXTURE_2D); 

glBegin(GL_TRIANGLES); 
for (int ii=0;ii<triangle.size();++ii) { 
    for (int jj=0;jj<3;++jj) { 
    glTexCoord2d(triangle[ii].tex[jj][0],triangle[ii].tex[jj][1]); 
    glVertex2d(triangle[ii].point[jj[0],triangle[ii].point[jj][1]); 
    } 
} 
glEnd(); 

Dove triangle è una certa struttura di dati che hai fatto tenendo i vertici del triangolo e mappature associate nell'immagine. La scheda grafica gestirà l'interpolazione dei pixel per te.

+0

Grazie mille per la tua risposta! Questo è molto interessante. "Sfortunatamente, la curva di apprendimento per OpenGL inizia un po 'in salita". Immagino che questo sia il problema per me! Ma probabilmente dovrò scoprirlo a un certo punto. – Seub

+0

Utilizza codice deprecato. Vuoi davvero usare shader e campionatori di texture. – doron

+0

@doron È deprecato. Ma se la funzionalità della pipeline fissa farà il lavoro, è molto più facile andare in quel modo. GLSL aumenta solo la difficoltà di apprendere la programmazione 3d. Offre maggiore flessibilità, ma anche il codice di assemblaggio. Tuttavia, c'è un motivo per cui molti di noi non programmano troppo in assemblea. – JCooper

1

Un'altra opzione oltre a OpenGL consiste nell'utilizzare OpenCL, che potrebbe essere più semplice per voi. Devi solo mappare la memoria della bitmap di input/output alla scheda grafica, scrivere un piccolo kernel in C che gestisca un triangolo e quindi accodare un'esecuzione del kernel per ogni triangolo. Funzionerà fino a 100 volte più veloce di un singolo core sulla CPU.

C'è un wrapper Qt per l'host API OpenCL qui:

http://doc.qt.digia.com/opencl-snapshot/index.html

0

Un approccio diverso è quello di sfruttare il ritaglio e le trasformazioni già implementato in modo efficiente all'interno del motore di vernice raster. Finché la trasformazione tra i due triangoli può essere espressa utilizzando una matrice di trasformazione aumentata 3x3, è sufficiente impostarla sul pittore di destinazione e quindi disegnare l'intera immagine di origine sul target. Verrà tagliato e trasformato per riempire il triangolo bersaglio. Puoi anche semplicemente disegnare il rettangolo di delimitazione del triangolo sorgente, invece dell'intera immagine, se la profilatura mostra un vantaggio.

Questo può essere parallelizzato in modo da elaborare tanti triangoli in parallelo quanti sono i core della CPU.