2009-07-21 20 views
5

Estoy usando pygame (1.9.0rc3, aunque esto también ocurre en 1.8.1) para crear un mapa de calor. Para construir el mapa de calor, utilizo una pequeña, de 24 bits de imagen PNG 11x11px punto con un fondo blanco y un punto gris muy baja opacidad que se detiene exactamente en los bordes:Pygame y Blitting: blanco sobre blanco = gris?

Dot image http://img442.imageshack.us/img442/465/dot.png

El área alrededor del punto es blanco perfecto, #ffffff, como debería ser. Sin embargo, cuando uso pygame para dividir la imagen varias veces en una nueva superficie con BLEND_MULT, aparece un cuadrado gris, como si el fondo del punto no fuera blanco perfecto, lo cual no tiene sentido.

el siguiente código, además de las imágenes incluidas, puede reproducir este:

import os 
import numpy 
import pygame 

os.environ['SDL_VIDEODRIVER'] = 'dummy' 
pygame.display.init() 
pygame.display.set_mode((1,1), 0, 32) 

dot_image = pygame.image.load('dot.png').convert_alpha() 

surf = pygame.Surface((100, 100), 0, 32) 
surf.fill((255, 255, 255)) 
surf = surf.convert_alpha() 

for i in range(50): 
    surf.blit(dot_image, (20, 40), None, pygame.BLEND_MULT)  

for i in range(100): 
    surf.blit(dot_image, (60, 40), None, pygame.BLEND_MULT)  

pygame.image.save(surf, 'result.png') 

Cuando se ejecuta el código, recibirá la siguiente imagen:

Resulting image after blending http://img263.imageshack.us/img263/4568/result.png

¿Hay una razón este sucede? ¿Cómo puedo solucionarlo?

Respuesta

6

Después de probar, lo único que pude ver fue que está 100% en lo cierto. La multiplicación por 255 resultados en una resta de 1 - cada vez. Al final, he descargado el código fuente de pygame, y la respuesta es allí mismo, en surface.h:

#define BLEND_MULT(sR, sG, sB, sA, dR, dG, dB, dA) \ 
    dR = (dR && sR) ? (dR * sR) >> 8 : 0;   \ 
    dG = (dG && sG) ? (dG * sG) >> 8 : 0;   \ 
    dB = (dB && sB) ? (dB * sB) >> 8 : 0; 

Pygame objetivo la aplicación de multiplicar la mezcla como

new_val = old_dest * old_source/256 

y no, que sería la forma correcta, como

new_val = old_dest * old_source/255 

Esto probablemente se hace para fines de optimización: un cambio de bit es mucho más rápido que una división. Como la proporción 255/256 es muy cercana a uno, la única diferencia que esto hace es un "apagado por uno": el valor que obtiene es el valor esperado menos uno, excepto si esperaba cero, en cuyo caso el resultado es correcto.

Así, se obtienen las siguientes posibilidades:

  1. ignorarlo, porque el off-by-one no tiene importancia para la mayoría de los propósitos.
  2. Agregue 1 a todos los valores de resultado. Más cercano al resultado esperado, excepto que pierdes el cero.
  3. Si la corrección en general no es importante, pero necesita 255 * 255 == 255 (ya sabe a qué me refiero), ORing 1 en lugar de agregar suficientes, y es más rápido.

Tenga en cuenta que si no elige la respuesta 1, por razones de rendimiento, probablemente tenga que escribir una extensión C en lugar de usar Python directamente.

+0

Hace solo dos minutos no me di cuenta de lo mismo (que está "apagado en uno"), pero no pude encontrar en qué ubicación de la fuente de pygame se encontraba el cálculo. Dado que este error hace una gran diferencia al multiplicar 255 * 255 (lo necesito para los mapas de calor), ¿cómo hago para implementar la opción n. ° 3? No estoy familiarizado con C, de modo que si pudiera orientarme en la dirección correcta con la implementación, probablemente salvaría mi cordura (o lo que queda, en este punto). –

+0

Probablemente, el mejor compromiso entre rendimiento y simplicidad sería implementar el # 2 mediante ADD-bliting de una superficie all (1,1,1) en el resultado. Sin embargo, si quieres profundizar en las extensiones C, puedes comenzar mirando cython, que es un C-Python-Hybrid: http://www.cython.org. Tenga en cuenta que yo mismo no tengo mucha experiencia en esa área. – balpha

+0

Además, si esto es factible en su caso, puede simplemente cambiar y recompilar pygame. Sin embargo, eso dificultaría la distribución de su programa, ya que terminaría con una versión no estándar de pygame. – balpha

1

También encontré este problema haciendo heatmaps y después de leer la respuesta de balpha, optó por arreglarlo de la manera "correcta" (si es más lenta).Cambiar las diversas

(s * d) >> 8 

a

(s * d)/255 

Esto requirió de parches funciones se multiplican en alphablit.c (aunque remendé surface.h también). No estoy seguro de cuánto afecta esto el rendimiento, pero para la aplicación específica (mapa de calor), produce imágenes mucho más bonitas.