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'
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
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.
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]))
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.
Eric
bien sur cela ne mache qu'en partant d'une image initialement en niveaux de gris....
Eric2
bien sur cela ne mache qu'en partant d'une image initialement en niveaux
de gris....
bien sur cela ne mache qu'en partant d'une image initialement en niveaux de gris....
Eric2
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
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
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