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'

10 réponses

1 2 3
Avatar
jean-michel bain-cornu
Eric Deveaud wrote:
je propose pour illustrer les 4 codes suivants et leur bench respectifs
def test4():
return [choice[o(pix) < 128] for pix in data1[::3]]
Il faut juste ajouter le join dans test4() :

return ''.join([choice[o(pix) < 128] for pix in data1[::3]])

il y a tout de meme une sacré echelle de différence non ??
...et c'est effectivement plus que satisfaisant.

J'ai intégré ce code dans l'appli que j'ai utilisé pour faire le test
avec wx, et les images sont affichées maintenant dans un temps tout à
fait acceptable compte tenu de leur taille (qlques secondes).
De plus, les solutions évoquées m'ouvrent des horizons pour d'autres
optimisations.
Si quelqu'un est intéressé par un code complet et autonome avec wx pour
transformer une série d'images, il n'a qu'à demander.
Merci pour toutes les réponses.
A+
jm
PS: autre solution évoquée par un ami: le regexp ; mais est-ce vraiment
pythonien ?

Avatar
Eric Deveaud
Laurent Pointal wrote:

Je n'ai pas le même niveau de différence.
Ton script, en repassant en 10**6 pour pouvoir comparer avec les tests
que j'ai fait un peu avant:

C:dev>python grisailles.py
test 1 9.383566
test 2 1.359730
test 3 1.264727
test 4 0.786419


avec 10^6 j'obtiens ceci

hebus:check/work_dir > python test.py
test 1 184.710757
test 2 3.726266
test 3 3.478191
test 4 2.855085

hebus:check/work_dir > python2.3 test.py
test 1 185.339335
test 2 3.666680
test 3 3.422132
test 4 2.847318


ta machine est bougrement plus rapide que la miene ;-))

Eric

Avatar
Eric Deveaud
jean-michel bain-cornu wrote:
Eric Deveaud wrote:
je propose pour illustrer les 4 codes suivants et leur bench respectifs
def test4():
return [choice[o(pix) < 128] for pix in data1[::3]]
Il faut juste ajouter le join dans test4() :

return ''.join([choice[o(pix) < 128] for pix in data1[::3]])


ooooops

PS: autre solution évoquée par un ami: le regexp ; mais est-ce vraiment
pythonien ?


dans la vraie vie quand tu comences a chercher a optmiser,
j'aurais tendance a dire que les pythonisme tu t'en fout un peu ;-)
dans le pire des cas tu fini par fiare un module en C que tu linke avec ton
code python (cf swig)

petite questions comment acquiers tu les valeurs de tes images ?
lecture classique / mmap ?? il doit y avoir encore des pouiemmes a grapiller de
ce cote

Eric


Avatar
jean-michel bain-cornu
Eric Deveaud wrote:
petite questions comment acquiers tu les valeurs de tes images ?
lecture classique / mmap ?? il doit y avoir encore des pouiemmes a grapiller de
ce cote
Pas vraiment je pense.

C'est fait avec la classe Image de wx :
img= wx.Image('photo.jpg',wx.BITMAP_TYPE_JPEG)
data1= img.GetData() # obtient un array data1
...passage en blanc/noir...
img.SetData(data2) # attends un data2 string
bmp= wx.BitmapFromImage(img)
stbmp= wx.StaticBitmap(parent,wx.ID_ANY,bmp)
..etc..
ce qui fait que je n'ai pas trop la main sur ce qui se fait et qui est
d'ailleurs très rapide (sans traitement, l'affichage des images est
instantané, même avec un processeur en bois).
A+
jm

Avatar
aaa
encore un peu plus rapide en remplacant la fin de test4():

choice = ( blanc, noir)
# astuce pour eviter le look ahead ... maintenant inutile
carac=chr(128)
return [choice[pix < carac] for pix in data1[::3]]

on evite ainsi le ord()....




Eric2
Avatar
jean-michel bain-cornu
wrote:
encore un peu plus rapide en remplacant la fin de test4():

choice = ( blanc, noir)
# astuce pour eviter le look ahead ... maintenant inutile
carac=chr(128)
return [choice[pix < carac] for pix in data1[::3]]

on evite ainsi le ord()....




Eric2
Et ça rend le code un petit peu plus clair. Judicieux.

Merci.
jm

Avatar
Eric Deveaud
wrote:
encore un peu plus rapide en remplacant la fin de test4():

choice = ( blanc, noir)
# astuce pour eviter le look ahead ... maintenant inutile
carac=chr(128)
return [choice[pix < carac] for pix in data1[::3]]

on evite ainsi le ord()....



joli et un code plus facile a lire.

Eric

--
Ça fait des années que septembre dure 12 mois.
Cette année, cependant, les cons de l'année prochaine sont en avance.
-+- CHC in GCU -- Y'a plus d'saisons ma pôv dame -+-


Avatar
Eric
Eric Deveaud wrote:

je propose pour illustrer les 4 codes suivants et leur bench respectifs
def test4():
return [choice[o(pix) < 128] for pix in data1[::3]]


Il faut juste ajouter le join dans test4() :
return ''.join([choice[o(pix) < 128] for pix in data1[::3]])

il y a tout de meme une sacré echelle de différence non ??


...et c'est effectivement plus que satisfaisant.
J'ai intégré ce code dans l'appli que j'ai utilisé pour faire le test
avec wx, et les images sont affichées maintenant dans un temps tout à
fait acceptable compte tenu de leur taille (qlques secondes).
De plus, les solutions évoquées m'ouvrent des horizons pour d'autres
optimisations.
Si quelqu'un est intéressé par un code complet et autonome avec wx pour
transformer une série d'images, il n'a qu'à demander.


bien sur que l'on est interessé ! Ca serait bien de voir ce fameux code....


Merci pour toutes les réponses.
A+
jm
PS: autre solution évoquée par un ami: le regexp ; mais est-ce vraiment
pythonien ?



Eric2


Avatar
jean-michel bain-cornu
Bonjour,
Eric wrote:
Si quelqu'un est intéressé par un code complet et autonome avec wx
pour transformer une série d'images, il n'a qu'à demander.


bien sur que l'on est interessé ! Ca serait bien de voir ce fameux code....
Voilà donc le code ci-après.

J'ai essayé d'épurer au maxi afin de ne conserver que la partie imagerie.
C'est pas sorcier, finalement (environ 30 lignes). J'ai connu des gui
plus gourmands... Quelqu'un peut-il faire mieux avec un autre GUI ?
A+
jm
PS: je le mets dans le wiki dès que j'ai une minute.


# -*- coding: iso-8859-1 -*-
import wx
#----------------------------------------------------------------------
# nb: le fichier jpeg doit être présent
class mainWindow(wx.Frame):
def __init__(self):
wx.Frame.__init__(self,None,wx.ID_ANY,'Affichage images')
#--le sizer va servir à positionner les images et dimensionner
la fenêtre
sizerH= wx.BoxSizer(wx.HORIZONTAL)
#--l'image est d'abord affichée telle que...
img= wx.Image('amélie.jpg',wx.BITMAP_TYPE_JPEG)
bmp= wx.BitmapFromImage(img)
staticBmp= wx.StaticBitmap(self,wx.ID_ANY,bmp)
sizerH.Add(staticBmp)
#--...puis modifiée pour ne conserver que du blanc et du noir
data1= img.GetData()
blanc= chr(255)*3
noir= chr(0)*3
choice= (blanc, noir)
seuil= chr(128) # changer la valeur pour un résultat différent
(un seuil élevé pour une image pâle)
data2= ''.join([choice[pix < seuil] for pix in data1[::3]])
img.SetData(data2)
bmp= wx.BitmapFromImage(img)
staticBmp= wx.StaticBitmap(self,wx.ID_ANY,bmp)
sizerH.Add(staticBmp)
#--régler la taille de la fenêtre
self.SetSizer(sizerH)
self.SetAutoLayout(1)
sizerH.Fit(self)
#--
self.Show(True)
#----------------------------------------------------------------------
app = wx.PySimpleApp()
frame=mainWindow()
app.MainLoop()
del app
#----------------------------------------------------------------------


Avatar
Eric
merci, très sympa.

Eric2
1 2 3