segunda-feira, 16 de novembro de 2015

Imagens à roda

Todos nos habituámos a brincar com programas que manipulam imagens. Com frequência queremos um programa que roda uma imagem de 90 ou de 180 graus. Vamos ver como podemos resolver o primeiro caso. Depois o segundo é trivial.

Já discutimos como podemos representar uma imagem a preto e branco em Python: uma lista de listas. Para resolver este problema sabemos que temos que percorrer toda a imagem e para cada elemento da imagem saber qual será a sua nova posição. Mas no caso de uma rotação de 90 graus isso é fácil de resolver se olharmos globalmente para a matriz que representa a nossa imagem: a coluna i da matriz vai passar a ser a linha i da nova matriz… Daí a solução seguinte:
01.def roda_90(imagem):
02.    """Baseia-se na construção da transposta da imagem vista como uma matriz."""
03.    imagem_aux = list()
04.    # troca colunas por linhas = transpõe
05.    for coluna in range(len(imagem[0])):
06.        nova_linha = list()
07.        for linha in imagem:
08.            nova_linha.append(linha[coluna])
09.        imagem_aux.append(nova_linha)
10.    # inverte dentro das linhas
11.    for linha in range(len(imagem_aux)):
12.        imagem_aux[linha] = imagem_aux[linha][::-1]
13.    return imagem_aux
Esta solução numa ideia simples: percorrer por colunas e para cada coluna ir buscar os elementos das diferentes linhas. O modo como fazemos isto obriga-nos no final a inverter todas as linhas. Esta ultima parte não é simpática. Mas podemos mudar as coisas.
01.def roda_90_b(imagem):
02.    """Baseia-se na construção da transposta da imagem vista como uma matriz."""
03.    imagem_aux = list()
04.    # troca colunas e linhas = transpõe
05.    for coluna in range(len(imagem[0])):
06.        nova_linha = list()
07.        for linha in imagem:
08.            nova_linha = [linha[coluna]] + nova_linha
09.        imagem_aux += [nova_linha]
10.    return imagem_aux
Como se pode ver, em vez de usarmos append, que coloca sempre no final, usamos a operação de concatenação de listas (+), colocando o novo elemento sempre à cabeça, o que permite ter no final a nova linha já invertida.

Como temos vindo a explicar ao longo destes pequenos posts há sempre muitas alternativas. Algumas fazem uso de um conhecimento mais profundo de Python. É o caso da solução que apresentamos a seguir.
1.def roda_90_c(imagem):
2.    copia = copy.deepcopy(imagem)
3.    transposta = list(zip(*copia))
4.    final = [linha[::-1] for linha in transposta]
5.    return final
Neste exemplo, usamos um argumento na forma *copia. Isto significa que o objecto copia vai ser desmembrado nos seus elementos e é sobre esses elementos que vai ser aplicada a função zip. Deste modo obtemos a transposta. Depois é só inverter cada linha usando listas por compreensão. Refira-se finalmente que usámos uma cópia profunda da imagem inicial para não a destruir.

Sem comentários:

Enviar um comentário