OVH Cloud OVH Cloud

Un petit pb de rapidité

25 réponses
Avatar
jean-michel bain-cornu
Bonjour,
Récemment s'est posé la question de convertir une image grise en 2
niveaux de noir et blanc. Une des solutions consiste à traiter l'image
avec une boucle.
Le pb est que si l'image est grosse (2 mégas pixels dans l'exemple
ci-après), le traitement est long (environ 20 secs sur un PC moyen).
Sachant que la boucle est un bête test du premier octet de chaque groupe
de trois octets, suivis d'une concaténation de chaîne, quelqu'un
connaît-il un moyen d'aller plus vite ?
Merci
jm

# -*- coding: iso-8859-1 -*-
from array import *
print 'début'
grisClair= chr(240)*3
grisFonce= chr(10)*3
data1= array('c',(grisClair+grisFonce)*10**6) # crée un array contenant
un buffer de 2 millions de pixels RVB gris
data2= ''
blanc= chr(255)*3
noir= chr(0)*3
# boucle sur chaque groupe de 3 octets du buffer
for pixind in range(0,len(data1),3):
if ord(data1[pixind]) < 128:
data2=data2+noir
else:
data2=data2+blanc
# data2 est un string contenant un buffer de 2 millions de pixels blancs
ou noirs
print 'fin'

5 réponses

1 2 3
Avatar
Eric
on peut encore faire un chouilla mieux en utilisant un dictionnaire:

def test5():
choix = ( chr(255)*3, chr(0)*3) # blanc et noir
table={}
for i in range(256): # on prepare le dico
table[chr(i)]=choix[i<128]

grisClair= chr(240)*3
grisFonce= chr(10)*3
data1= array('c',(grisClair+grisFonce)*10**6) # OP was 106

return ''.join([table[pix] for pix in data1[::3]])


on reduit dans ce cas encore d'environ 15%

mais il y a encore peut etre beaucoup mieux si je ne me trompe pas :

def test6():

grisClair= chr(240)*3
grisFonce= chr(10)*3

data1= (grisClair+grisFonce)*10**6 # OP was 106

return data1.translate(chr(0)*128+chr(255)*128)


Dans ce cas la, on divise encore le temps de calcul par plus de 10...
A moins que je ne me trompe :-) Dites moi ?


Eric2
Avatar
Eric
on peut encore faire un chouilla mieux en utilisant un dictionnaire:

def test5():
choix = ( chr(255)*3, chr(0)*3) # blanc et noir
table={}
for i in range(256): # on prepare le dico
table[chr(i)]=choix[i<128]

grisClair= chr(240)*3
grisFonce= chr(10)*3
data1= array('c',(grisClair+grisFonce)*10**6) # OP was 106

return ''.join([table[pix] for pix in data1[::3]])


on reduit dans ce cas encore d'environ 15%

mais il y a encore peut etre beaucoup mieux si je ne me trompe pas :

def test6():

grisClair= chr(240)*3
grisFonce= chr(10)*3

data1= (grisClair+grisFonce)*10**6 # OP was 106

return data1.translate(chr(0)*128+chr(255)*128)


Dans ce cas la, on divise encore le temps de calcul par plus de 10...
A moins que je ne me trompe :-) Dites moi ?


Eric2
Avatar
Christophe
Pour aller encore un peu plus vite, on peut aussi éviter la
comparaison en construisant un tableau qui contient les couleurs en
fonction du pixel (les 127 premiers éléments sont noir et les
suivants sont blanc). Comme on a besoin d'entiers pour indicer le
tableau (l'utilisation d'un dictionnaire serait plus couteuse), on peut
supprimer l'utilisation de ord en construisant un tableau d'entier dès
le début (type 'B' au lieu de 'c'). Dernière optimisation :
utilisation de map pour faire une boucle en C plutôt qu'en Python.
Voici par exemple une telle solution :

def test6():
# idem en utilisant une table de conversion + tableau d'entiers +
map
grisClair= chr(240)*3
grisFonce= chr(10)*3
data1= array('B',(grisClair+grisFonce)*N) # OP was 10^6
blanc= chr(255)*3
noir= chr(0)*3
conv = (noir,)*127+(blanc,)*129
return ''.join(map(conv.__getitem__, data1[::3]))


Christophe.
Avatar
Eric
bien sur cela ne mache qu'en partant d'une image initialement en niveaux
de gris....

Eric2
Avatar
jean-michel
Eric wrote:
bien sur cela ne mache qu'en partant d'une image initialement en niveaux
de gris....

Eric2
C'était bien entendu comme ça. Ceci étant, la conversion à partir d'une

image couleur ne devrait pas être trop difficile non plus. D'après
linuxgraphic.org, la formule de conversion utilisée par gimp est Y =
0.3R + 0.59G + 0.11B. Pas de quoi se luxer le cerveau ;-)
A+
jm

1 2 3