Bienvenue sur IndexError.

Ici vous pouvez poser des questions sur Python et le Framework Django.

Consultez la FAQ pour améliorer vos chances d'avoir des réponses à vos questions.

Parcourir un tableau multidimensionnel

+4 votes

J'ai un tableau du genre matrice[5][5][3] qui représente une image de 5 pixel par 5 pixel.
Je le parcours avec des boucles for comme ceci :

range_pixel = range(0,5)
range_color = range(0,3)
for x in range_pixel:
    for y in range_pixel:
        for color in range_color:
            matrice[x][y][color] = truc #calculs impliquant x y et color

pour faire des calculs sur les pixels, mais c'est lent, y'a t-il un moyen rapide de parcourir ce genre de tableau, ou alors il faudrait faire complétement autrement ?
Les calculs me permettent d'animer les images pour les afficher sur une matrice de LEDs.
Quand je dis que c'est lent, sur un ordinateur a peu près décent, ça passe bien. Mais sur un Raspberry, ça prend pas mal de temps, et les animations ne sont plus très fluides :/

demandé 6-Jun-2015 par s7ef (130 points)

2 Réponses

+3 votes
 
Meilleure réponse

En numpy, le principe est d'éviter de faire des boucles sur les tableaux soi-même.
Ca donne du code qui va à l'essentiel et ça profite par derrière de routines compilées qui font les boucles pour nous (plus rapide).

Par exemple, on pourra faire comme ça:

import numpy as np

# préparation des tableaux
X = np.arange(dim).reshape((-1, 1, 1))
Y = np.arange(dim).reshape((1, -1, 1))
C = np.arange((3).reshape((1, 1, -1))
matrice = n.empty((dim, dim, 3))

# update du tableau matrice
matrice[:] = faire_un_truc(X, Y, C)

La fonction faire_un_truc() peut utiliser les opérateurs mathématiques :

def faire_un_truc(X, Y, C):
    return X * (3 * dim) + Y * 3 + C

mais aussi des fonctions mathématiques plus sophistiquées pour peu qu'on utilise celles de numpy (plutôt que du module math):

def faire_un_truc(X, Y, C):
    return np.sqrt((X - 2)**2 + (Y - 2)**2)
répondu 8-Jun-2015 par bubulle (2,256 points)
sélectionné 8-Jun-2015 par s7ef

Je vais creuser ça, ça à l'air prometteur :) Surtout si ça me débarrasse des boucles for. merci bien

+4 votes

Pour un pc, le module multiprocessing pourrait peut être accélérer tes calculs.
tu peux voir ce post:
http://indexerror.net/1530/hyper-acceleration-numba-x-multiprocessing?show=1530#q1530

Pour ton raspberry, essayes plutôt avec la bibliothèque numpy.

Normalement ça doit marcher.

répondu 6-Jun-2015 par buffalo974 (2,952 points)

Mouais...
le raspberry2 a quand même 4 coeurs, autant qu'un pc décent. ya moyen de multi-threader ;)

Pour le moment, ce n'est qu'un Raspberry B (version 1).
J'ai déjà des threads dans mon programme, avec le module threading, j'avais tenté de lancer un thread de calcul par ligne, mais c'était beaucoup plus lent.
J'avais aussi acheté le LinuxMag, et je n'ai pas réussi a améliorer tant que ça les perfs. Peut-être que je l'ai lu de travers, je vais m'y replonger, mais de mémoire, il faut que j’oublie toutes les techniques d'accélération GPU, ce qui enlève une bonne moitié de l'article en question.

si il y a des curieux, le code en question est là : https://bitbucket.org/stefan_jamet/daftduino.py/src/b5112a44abf30b4a5cdf1d38725170064a323e53/DaftDuino_Server/anims/pulsations.py?at=v4
et une démo est là : http://ddo.s7ef.fr/ (mettre "demo" dans la case)

Pour numpy, c'est pareil, je ne suis pas du tout convaincu : ce petit test ne le donne absolument pas gagnant (a moins que je m'y prenne mal) :

import timeit
import numpy as np

dim = 5
nb_tests = 1000

range_dim = range(0, dim)
range_color = range(0, 3)

matrice_t1 = []
matrice_t1.extend([[[0,0,0] for i in range_dim] for j in range_dim])

matrice_t2 = np.zeros(shape=(dim, dim, 3))

def t1():
    for x in range_dim:
        for y in range_dim:
            for color in range_color:
                matrice_t1[x][y][color] = x*dim*3 + y*3 + color

def t2():
    for x in range_dim:
        for y in range_dim:
            for color in range_color:
                matrice_t2[x][y][color] = x*dim*3 + y*3 + color

print("1: " + str(timeit.Timer(t1).timeit(number=nb_tests)))
print("2: " + str(timeit.Timer(t2).timeit(number=nb_tests)))

1: 0.029767342000923236
2: 0.059689112998967175

c'est pas comme ça qu'on fait du numpy.
essaie avec quelque chose comme ça :
(pas testé)

X = np.arange(dim).reshape((-1, 1, 1))
Y = np.arange(dim).reshape((1, -1, 1))
C = np.arange((3).reshape((1, 1, -1))
mat = X * (3 * dim) + Y * 3 + C

en effet, c'est très rapide. Sauf que je ne vois pas comment adapter ça à mon code, l'exemple de matrice[x][y][color] = xdim3 + y*3 + color, c'était juste pour utiliser les coordonnées et les couleurs, en vrai je calcule plutôt des distances par rapport a un point pour tracer des cercles (autant que possible sur un carré de 5 pixels :))
dans ce genre là :

from math import *

dim = 5
nb_tests = 1000

range_dim = range(0, dim)
range_color = range(0, 3)

CentreX = 2
CentreY = 2

Rayon = 2

matrice_t1 = []
matrice_t1.extend([[[0,0,0] for i in range_dim] for j in range_dim])

def t1():
    for x in range_dim:
        for y in range_dim:
            RayonCalc = sqrt((x-CentreX)**2+(y-CentreY)**2)
            for color in range_color:
                matrice_t1[x][y][color] = RayonCalc

enfin, en gros.

Je vois. J'ai développer un peu dans ma réponse.

la méthode de bubulle fonctionne toujours pour remplir le tableau

mat = np.sqrt( (X - CentreX)**2 + (Y - CentreY)**2) + 0.*C
...