c++ - gui - pyqt qpainter
Qt/C++: dibujar eficientemente (5)
OpenGL hace que la imagen (textura) coordine la asignación realmente bien. Probablemente quieras usar alguna forma de OpenGL. Qt tiene alguna vinculación con OpenGL que puede ayudarte.
He diseñado un programa que, básicamente, corta una forma geométrica en muchos triángulos pequeños (en un "lienzo izquierdo"), aplica una transformación matemática simple al grupo de triángulos y los redibuja en su nueva configuración. Ver captura de pantalla a continuación.
Para dibujar estos triángulos, uso QPainter::drawPolygon
. Cada triángulo de la derecha corresponde a un triángulo de la izquierda, así que sé qué color quiero usar para dibujarlo.
Hasta ahora, bien. Aunque dibuje muchos más triángulos que esto (cuando uso triángulos mucho más pequeños para cortar la forma), esto es lo suficientemente rápido.
He agregado una función a mi programa: puedo dibujar triángulos extraídos de una imagen en lugar de triángulos simples: vea la siguiente captura de pantalla.
El problema es que la forma en que hago esto es demasiado lenta. Así es como lo hago:
- Corro por todos los triángulos.
- Para cada triángulo, calculo las coordenadas de cada píxel que se mostrará.
- Para cada uno de estos píxeles, calculo las coordenadas del píxel correspondiente en la imagen (esta es una operación matemática fácil), y recupero el color de ese píxel.
- Uso
QPainter::setPen(QColor)
yQPainter::drawPoint(QPoint)
para dibujar el píxel.
Soy nuevo en la programación en Qt y no sé nada de gráficos, así que esto es lo que podría encontrar. El problema es que es "inaceptablemente" demasiado lento (el paintEvent
de cada lienzo toma alrededor de 0.15s, en comparación con 0.01s en el caso de los triángulos lisos).
Corrí un perfilador para tratar de entender lo que está pasando, noté que en el paintEvent
del widget del paintEvent
,
- El 58% del tiempo se gasta en
QPainter::drawPoint
- 27% del tiempo se gasta en
QPainter::setPen
Parece que QPainter::drawPoint
es demasiado complicado y lento: solo quiero que imprima un píxel de un color determinado, eso es todo.
Es posible que haya encontrado una solución a mi problema: almacenar una QImage
(como una variable miembro de mi widget de lienzo) que represente todo lo que quiero que muestre mi lienzo, y definirlo completamente en mi paintEvent
pixel por pixel, y luego dibujarlo inmediatamente al final de mi paintEvent
con QPainter::drawImage
. Tengo un indicio de que esto será mucho más rápido. Pero antes de volver a escribir mi código de nuevo, me gustaría saber si eso es realmente lo que quiero hacer.
Espero no haberte aburrido de hacer la muerte! Muchas gracias de antemano por sus ideas.
Otra opción aparte de OpenGL es usar OpenCL, que puede ser más fácil para usted. Solo tiene que asignar en memoria el mapa de bits de entrada / salida a la tarjeta gráfica, escribir un pequeño kernel en C que maneje un triángulo y luego poner en cola una ejecución del kernel para cada triángulo. Esto funcionará hasta 100 veces más rápido que un solo núcleo en la CPU.
Hay un contenedor Qt para la API de host OpenCL aquí:
Solución no OpenGl:
Utilice un búfer RGB para la imagen de destino. Trabaja a través de tus 3 primeros pasos como lo hiciste antes. Una vez que haya encontrado la posición y el color del píxel, configúrelo en este búfer. Entonces usas
QImage::QImage ( uchar * data, int width, int height, Format format )
Para construir la imagen basada en el búfer anterior. Está cerca de la solución que proporcionó y será mucho más rápida que la que tiene actualmente.
Un enfoque diferente es aprovechar el recorte y las transformaciones ya implementadas de manera eficiente dentro del motor de pintura de trama. Siempre que la transformación entre los dos triángulos se pueda expresar utilizando una matriz de transformación aumentada de 3x3, simplemente debe establecerla en el pintor de destino y luego dibujar la imagen de origen completa en el objetivo. Se recortará y se transformará para llenar el triángulo objetivo. También puede simplemente dibujar el rectángulo delimitador del triángulo de origen, en lugar de la imagen completa, si el perfil le muestra una ventaja.
Esto se puede paralelizar para procesar tantos triángulos en paralelo como núcleos de CPU.
Una forma de hacerlo sería utilizar una clase heredada de QGLWidget en lugar del combo QGraphicsScene / QGraphicsView. Desafortunadamente, la curva de aprendizaje para OpenGL comienza un poco empinada. Sin embargo, será muy rápido porque sucederá directamente en la tarjeta gráfica que está optimizada para este tipo de operación.
QGLWidget::bindTexture()
la imagen QGLWidget::bindTexture()
.
Asociará puntos de la imagen con su malla de triángulo y los enviará todos a su tarjeta gráfica. En la versión heredada de OpenGL (que es más fácil de usar que la API más nueva en mi opinión), se vería algo como esto:
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();
Donde triangle
es una estructura de datos que has hecho manteniendo los vértices de triángulos y las asignaciones asociadas en la imagen. La tarjeta gráfica manejará la interpolación de píxeles para usted.