Question

Ok, donc j'ai un morceau de code Python qui a vraiment besoin de l'optimisation.

  • Il est un jeu de vie itération sur une petite (80x60 pixels) image et extrait les valeurs RVB de celui-ci.
  • utilise actuellement imbriqués pour-boucles; Je préfère échanger sur les boucles pour de la fonction plus rapide map() c, mais si je fais ce que je ne peux pas comprendre comment je peux obtenir les valeurs x, y, ni les valeurs locales définies hors de la portée des fonctions I « d besoin de définir.
  • serait en utilisant map() être plus vite que ce jeu actuel pour les boucles? Comment pourrais-je l'utiliser et toujours obtenir x, y?
  • J'utilise actuellement des surfaces Pygame, et je l'ai essayé les modules surfarray/pixelarray, mais depuis que je change / obtenir chaque pixel, il est beaucoup plus lent que Surface.get_at()/set_at().
  • En outre, un peu hors de propos ... pensez-vous que cela pourrait être plus rapide si Python n'a pas été parcourait une liste de numéros mais juste incrémenter un numéro, comme dans d'autres langues? Pourquoi ne pas inclure un python ainsi que leur foreach normal () ()?
  • La quantité de conditionals il fait probablement des choses plus lent aussi, non? La partie la plus lente est la vérification des voisins (où il construit la liste n) ... Je l'ai remplacé que tralala avec accès tranche sur un tableau 2D, mais il ne fonctionne pas correctement.

Version expurgée du code:

xr = xrange(80)
yr = xrange(60)
# surface is an instance of pygame.Surface
get_at = surface.get_at()
set_at = surface.set_at()

for x in xr:
    # ....
    for y in yr:
        # ...
        pixelR = get_at((x,y))[0]
        pixelG = get_at((x,y))[1]
        pixelB = get_at((x,y))[2]
        # ... more complex stuff here which changes R,G,B values independently of each other
        set_at((x,y),(pixelR,pixelG,pixelB))

La version complète de la fonction:

# xr, yr = xrange(80), xrange(60)
def live(surface,xr,yr):
    randint = random.randint
    set_at = surface.set_at
    get_at = surface.get_at
    perfect = perfectNeighbours #
    minN = minNeighbours        # All global variables that're defined in a config file.
    maxN = maxNeighbours        #
    pos = actual                # actual = (80,60)
    n = []
    append = n.append
    NEIGHBOURS = 0

    for y in yr: # going height-first for aesthetic reasons.
        decay = randint(1,maxDecay)
        growth = randint(1,maxGrowth)

        for x in xr:
            r, g, b, a = get_at((x,y))

            del n[:]
            NEIGHBOURS = 0

            if x>0 and y>0 and x<pos[0]-1 and y<pos[1]-1:
                append(get_at((x-1,y-1))[1])
                append(get_at((x+1,y-1))[1])
                append(get_at((x,y-1))[1])
                append(get_at((x-1,y))[1])
                append(get_at((x+1,y))[1])
                append(get_at((x-1,y+1))[1])
                append(get_at((x+1,y+1))[1])
                append(get_at((x,y+1))[1])
                for a in n:
                    if a > 63:
                        NEIGHBOURS += 1

            if NEIGHBOURS == 0 and (r,g,b) == (0,0,0): pass
            else:

                if NEIGHBOURS < minN or NEIGHBOURS > maxN:
                    g = 0
                    b = 0
                elif NEIGHBOURS==perfect:
                    g += growth
                    if g > 255:
                        g = 255
                        b += growth
                        if b > growth: b = growth
                else:
                    if g > 10: r = g-10
                    if g > 200: b = g-100
                    if r > growth: g = r
                    g -= decay
                    if g < 0:
                        g = 0
                        b = 0
                r -= 1
                if r < 0:
                    r = 0
                set_at((x,y),(r,g,b))
Était-ce utile?

La solution

Puisque vous lisez et réécriture tous pixel, je pense que vous pouvez obtenir en ne pas utiliser un Surface la meilleure amélioration de la vitesse.

Je suggère d'abord prendre votre image 80x60 et le convertir en un fichier bitmap simple avec pixels 32 bits. Ensuite, lisez les données de pixels dans un python array objet. Maintenant, vous pouvez marcher sur l'objet array, la lecture des valeurs, le calcul de nouvelles valeurs, et les nouvelles valeurs poussant en place avec une vitesse maximale. Lorsque vous avez terminé, enregistrez votre nouvelle image bitmap, puis le convertir en un Surface.

Vous pouvez également utiliser des pixels 24 bits, mais cela devrait être plus lent. pixels 32 bits signifie un pixel est une valeur entière de 32 bits, ce qui rend la matrice de pixels beaucoup plus facile d'index. 24 bits des pixels emballés signifie que chaque pixel est de 3 octets, ce qui est beaucoup plus ennuyeux pour indexer.

Je crois que vous gagnerez beaucoup plus de vitesse sur cette approche que en essayant d'éviter l'utilisation de for. Si vous essayez cela, s'il vous plaît poster quelque chose ici pour nous le faire savoir à quel point cela a fonctionné ou n'a pas fait. Bonne chance.

EDIT: Je pensais qu'un array n'a qu'un seul indice. Je ne sais pas comment vous avez réussi à obtenir deux index pour travailler. Je vous attendais à faire quelque chose comme ceci:

def __i(x, y):
    assert(0 <= x < 80)
    assert(0 <= y < 60)
    i = (y*80 + x) * 4
    return i
def red(x, y):
    return __a[__i(x, y)]
def green(x, y):
    return __a[__i(x, y) + 1]
def blue(x, y):
    return __a[__i(x, y) + 2]
def rgb(x, y):
    i = __i(x, y)
    return __a[i], __a[i + 1], __a[i + 2]
def set_rgb(x, y, r, g, b):
    i = __i(x, y)
    _a[i] = r
    _a[i + 1] = g
    _a[i + 2] = b

# example:
r, g, b = rgb(23, 33)

Depuis un array Python ne peut contenir un seul type, vous souhaitez définir le type de « octet non signé », puis l'index comme je l'ai montré.

Si bien sûr __a est la variable array réelle.

Si rien de tout cela est utile, essayez de convertir votre bitmap dans une liste, ou peut-être trois listes. Vous pouvez utiliser des listes imbriquées pour obtenir d'adressage 2D.

J'espère que cela aide. Si ce n'est pas utile, alors je ne comprends pas ce que vous faites; si vous expliquez plus je vais essayer d'améliorer la réponse.

Autres conseils

Qu'est-ce qui rend votre code lent est probablement pas les boucles, ils sont incroyablement rapides.

Que fait votre code ralentit sont le nombre d'appels de fonction. Par exemple

pixelR = get_at((x,y))[0]
pixelG = get_at((x,y))[1]
pixelB = get_at((x,y))[2]

est beaucoup plus lent que (environ 3 fois je suppose)

r, g, b, a = get_at((x,y))

Chaque get_at, serrures d'appel set_at la surface, donc il est plus rapide d'accéder directement aux pixels en utilisant les méthodes disponibles. Celui qui est le plus raisonnable semble Surface.get_buffer .

Utilisation map ne fonctionne pas dans votre exemple, parce que vous avez besoin des index. Avec aussi peu que 80 et 60 chiffres, il pourrait même être plus rapide d'utiliser range() au lieu de xrange().

map(do_stuff, ((x, y) for x in xrange(80) for y in xrange(60)))

do_stuff serait vraisemblablement défini comme ceci:

def do_stuff(coords):
    r, g, b, a = get_at(coords)
    # ... whatever you need to do with those ...
    set_at(coords, (r, g, b))

Vous pouvez également utiliser une compréhension de liste au lieu d'une expression de générateur comme second argument à map (remplacer ((x, y) ...) avec [(x, y) ...]) et utiliser range au lieu de xrange. Je dirais qu'il est peu probable d'avoir un effet significatif sur les performances, cependant.

Modifier Notez que gs a certainement raison de la for boucles ne pas être la chose principale dans le besoin d'optimisation de votre code ... Réduire les appels superflus à get_at est plus important. En fait, je ne suis pas sûr de remplacer les boucles avec map va réellement améliorer les performances ici du tout ... Cela dit, je trouve la version map plus lisible (peut-être à cause de mon fond ... FP), alors là, vous aller de toute façon. ; -)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top