2009-07-21 19 views
5

Sto usando pygame (1.9.0rc3, anche se questo succede anche nella 1.8.1) per creare una heatmap. Per costruire la heatmap, io uso un piccolo, 24-bit 11x11px dot immagine PNG con uno sfondo bianco e un bassissimo-opacità punto grigio che si ferma esattamente ai bordi:Pygame e blitting: bianco su bianco = grigio?

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

L'area intorno al punto è bianco perfetto, #ffffff, come dovrebbe essere. Tuttavia, quando uso pygame per fondere più volte l'immagine su una nuova superficie utilizzando BLEND_MULT, appare un quadrato grigio, come se lo sfondo del punto non fosse bianco perfetto, il che non ha senso.

il seguente codice, più inclusi immagini, in grado di riprodurre questo:

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') 

Quando si esegue il codice, si otterrà la seguente immagine:

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

C'è un motivo questo accade? Come posso aggirarlo?

risposta

6

Dopo aver provato in giro, l'unica cosa che ho potuto vedere è che hai ragione al 100%. La moltiplicazione per 255 risulta in una sottrazione di 1 - ogni volta. Alla fine, ho scaricato il codice sorgente pygame, e la risposta è proprio lì, in 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 concretizzazione moltiplicano miscelazione come

new_val = old_dest * old_source/256 

e non, che sarebbe il modo corretto, come

new_val = old_dest * old_source/255 

Questo è probabilmente fatto per scopi di ottimizzazione - un po 'di spostamento è molto più veloce di una divisione. Poiché il rapporto 255/256 è molto vicino a uno, l'unica differenza che ne deriva è un "off by one": il valore che ottieni è il valore atteso meno uno, tranne se ti aspetti zero, nel qual caso il risultato è corretto.

Quindi, ci sono queste possibilità:

  1. ignorarlo, perché l'off-by-one non ha importanza per la maggior parte degli scopi.
  2. Aggiungi 1 a tutti i valori dei risultati. Più vicino al risultato previsto, tranne che si perde lo zero.
  3. Se la correttezza complessiva non è importante, ma è necessario 255 * 255 == 255 (sai cosa intendo), ORing 1 invece di aggiungere è sufficiente, ed è più veloce.

Nota che se non scegli la risposta 1, per ragioni di prestazioni probabilmente dovrai scrivere un'estensione C invece di usare direttamente Python.

+0

Non solo due minuti fa ho realizzato la stessa cosa (che è "off by one"), ma non sono riuscito a trovare dove si trovasse il calcolo nella fonte pygame. Poiché questo errore fa una grande differenza quando si moltiplica 255 * 255 (ne ho bisogno per le heatmap), come faccio a implementare l'opzione # 3? Non ho alcuna familiarità con C, quindi se potessi indicarmi la giusta direzione con l'implementazione, probabilmente salverei la mia sanità mentale (o ciò che è rimasto, a questo punto). –

+0

Il miglior compromesso tra prestazioni e semplicità sarebbe probabilmente l'implementazione n. 2 di ADD-bliting una superficie all-(1,1,1) sul risultato. Tuttavia, se vuoi scavare nelle estensioni C, potresti iniziare guardando cython, che è un C-Python-Hybrid: http://www.cython.org. Nota che io stesso non ho molta esperienza in quell'area. – balpha

+0

Inoltre, se questo è fattibile nel tuo caso, potresti semplicemente cambiare e ricompilare pygame stesso. Tuttavia, ciò renderebbe più difficile la distribuzione del programma, poiché si finirebbe con una versione non standard di pygame. – balpha

1

Anche questo problema si verificava con le heatmap e dopo aver letto la risposta di balpha, ha scelto di correggerlo nel modo "giusto" (se più lento).Modificare le varie

(s * d) >> 8 

a

(s * d)/255 

Ciò ha richiesto l'applicazione di patch funzioni moltiplicarsi in alphablit.c (anche se ho patchato surface.h pure). Non sono sicuro di quanto questo influenzi le prestazioni, ma per l'applicazione specifica (heatmap), produce immagini molto più belle.