Pergunta

Ok, então eu tenho um pedaço de código Python que realmente precisa de otimização.

  • É uma iteração Jogo da Vida sobre uma imagem pequena (80x60 pixels) e extrai os valores RGB a partir dele.
  • usando atualmente aninhados para loops; Eu prefiro trocar os loops para a função c mais rápido map(), mas se eu fizer isso eu não consigo descobrir como eu posso conseguir o x, y valores, nem os valores locais definidas fora do âmbito das funções I 'd necessidade de definir.
  • faria usando map() ser mais rápido do que este conjunto atual de loops? Como eu poderia usá-lo e ainda obter x, y?
  • Eu uso atualmente PyGame Superfícies, e eu tentei os módulos surfarray/pixelarray, mas desde que eu estou mudando / ficando cada pixel, que é muito mais lento do que Surface.get_at()/set_at().
  • Além disso, um pouco irrelevante ... você acha que isso poderia ser feito mais rápido se Python não estava atravessando uma lista de números, mas apenas incrementar um número, como em outros idiomas? Por python não inclui um normal para (), bem como a sua foreach ()?
  • A quantidade de condicionais provavelmente torna as coisas mais lento também, certo? A parte mais lenta é a verificação de vizinhos (onde ele cria a lista n) ... I substituiu o bit inteiro com acesso a fatia em uma matriz 2D, mas ele não funciona corretamente.

versão editada de código:

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))

A versão completa da função:

# 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))
Foi útil?

Solução

Uma vez que você está lendo e reescrevendo todas pixel, eu acho que você pode obter o melhor melhoria de velocidade, não usando um Surface.

Eu sugiro primeiro tomar sua imagem 80x60 e convertê-lo em um arquivo de bitmap simples, com 32 bits pixels. Então leia os dados de pixel em um array objeto python. Agora você pode andar sobre o objeto array, leitura de valores, calcular novos valores, e cutucando os novos valores no lugar com velocidade máxima. Quando terminar, salve sua imagem bitmap novo, e depois convertê-lo para um Surface.

Você também pode usar 24 bits pixels, mas que deve ser mais lento. 32 bits pixels meio de um pixel é um valor inteiro de 32-bit, o que faz com que a matriz de pixels muito mais fácil de índice. 24 bits embalado pixels meio cada pixel é 3 bytes, o que é muito mais irritante para indexar.

Eu acredito que você vai ganhar muito mais velocidade fora desta abordagem do que tentando evitar o uso de for. Se você tentar fazer isso, por favor postar algo aqui para que possamos saber o quão bem ele trabalhou ou não fez. Boa sorte.

EDIT: Eu pensei que um array tem apenas um único índice. Eu não sei como você conseguiu dois índices para o trabalho. Eu estava esperando que você faça algo parecido com isto:

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)

Uma vez que um array Python só pode ter um único tipo, você vai querer definir o tipo de "byte não atribuído" e, em seguida, índice como eu mostrei.

Onde é claro __a é a variável array real.

Se nada disso é útil, tente converter o bitmap em uma lista, ou talvez três listas. Você pode usar listas aninhadas para obter abordar 2D.

Espero que isso ajude. Se não for útil, então eu não estou entendendo o que está fazendo; se você explicar mais eu vou tentar melhorar a resposta.

Outras dicas

O que está fazendo o seu código lento provavelmente não é os loops, eles são incrivelmente rápido.

O que retarda feito seu código são o número de chamadas de função. Por exemplo

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

muito mais lento do que (cerca de 3 vezes eu acho)

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

Cada get_at, chamada set_at bloqueia a superfície, portanto, é mais rápido para acessar diretamente os pixels usando os métodos disponíveis. O que parece mais razoável é Surface.get_buffer .

Usando map não funciona no seu exemplo, porque você precisa os índices. Com apenas 80 e 60 números pode até ser mais rápido para uso range() vez de xrange().

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

onde do_stuff presumivelmente seria definida assim:

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

Você pode alternativamente usar uma compreensão de lista em vez de um gerador de expressão como o segundo argumento para map (substitua ((x, y) ...) com [(x, y) ...]) e uso range vez de xrange. Eu diria que não é muito provável que tenha um efeito significativo no desempenho, no entanto.

Editar: Note que gs é certamente certo sobre o for loops de não ser a principal coisa na necessidade de otimização em seu código ... Reduzir em chamadas supérfluo get_at é mais importante. Na verdade, eu não tenho certeza se substituindo os laços com map vai realmente melhorar o desempenho aqui em tudo ... Dito isto, acho que a versão map mais legível (talvez por causa do meu background FP ...), por isso aqui você ir de qualquer maneira. ; -)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top