Covil Do Dev

Fazendo a arte generativa “Schotter” em Python com numpy e opencv

Arte generativa é um tipo de arte que é feito em partes ou totalmente de forma automatizada. Neste Artigo vamos implementar uma arte generativa bem conhecida(“Georg Ness, Schotter”) em Python sem utilizar bibliotecas próprias para isso, apenas com manipulação de array com Numpy.


Lindomar Rodrigues
Lindomar Rodrigues

Atualizado

Para fazer artes generativas em Python podemos utilizar algumas bibliotecas próprias para isso, mas acredito que fazer com manipulação de array seja uma boa forma de fazer, pois permite aprender vários conceitos e técnicas de programação que podemos utilizar em outros contextos.

Nesse artigo vamos utilizar a biblioteca Numpy, caso você já conheça Python a algum tempo provavelmente já utilizou essa biblioteca alguma vez, os conceitos básicos da linguagem são indispensáveis para esse tutorial, recomendo especialmente esse artigo sobre listas em python.

Preparando o ambiente para o desenvolvimento da arte generativa

Antes de começarmos a desenvolver a arte generativa, precisamos preparar o ambiente para isso, vamos utilizar apenas duas bibliotecas, que são “opencv-python” e “numpy”. Para instalar as bibliotecas utilizaremos pip, mas você pode fazer com anaconda ou de outras formas caso queira, com pip faríamos da seguinte forma:

pip install opencv-python
pip install numpy

Além disso vamos criar um arquivo chamado main.py

Iniciando o desenvolvimento da arte generativa criando a imagem

Vamos começar a desenvolver a arte generativa, primeiro vamos importar as bibliotecas:

import cv2
import numpy as np

Além disso vamos utilizar as bibliotecas padrões do Pyhon math e random.

import math
import random

Primeiramente vamos criar uma imagem de 2340px de altura por 1080px de largura, após iremos colorir essa imagem de branco.

img = np.zeros((2340, 1080, 1), np.uint8)
img[:, :] = 255

Função para desenhar o retângulo

Vamos fazer uma função que desenha um retângulo angulado na imagem, vamos utilizar a biblioteca “cv2”, para isso vamos criar uma função chamada “draw_angled_rec”:

def draw_angled_rec(x0, y0, width, height, angle, img, color, line_w):
    _angle = angle * math.pi / 180.0
    b = math.cos(_angle) * 0.5
    a = math.sin(_angle) * 0.5
    pt0 = (int(x0 - a * height - b * width),
           int(y0 + b * height - a * width))
    pt1 = (int(x0 + a * height - b * width),
           int(y0 - b * height - a * width))
    pt2 = (int(2 * x0 - pt0[0]), int(2 * y0 - pt0[1]))
    pt3 = (int(2 * x0 - pt1[0]), int(2 * y0 - pt1[1]))

    cv2.line(img, pt0, pt1, color, line_w, lineType=cv2.LINE_AA)
    cv2.line(img, pt1, pt2, color, line_w, lineType=cv2.LINE_AA)
    cv2.line(img, pt2, pt3, color, line_w, lineType=cv2.LINE_AA)
    cv2.line(img, pt3, pt0, color, line_w, lineType=cv2.LINE_AA)

    return img

Gerando a arte

Agora vamos gerar a arte, primeiro iremos calcular a largura da imagem dividindo por 13 para termos o valor do lado dos quadrados:

lado_q = img.shape[1] / 13

Faremos dois loops, um para cada linha e outro para cada coluna, para desenhar os quadrados vamos utilizar a função “draw_angled_rec”, e iremos gerar o ângulo baseado em uma variável chamada “emb” de embaralhamento, que será incrementada no final de cada linha da imagem, ou seja, ficará mais embaralhado cada vez que uma linha foi desenhada.

emb = 0
for y in range(int(lado_q // 2), img.shape[0] - int(lado_q), int(lado_q)):
    for x in range(int(lado_q // 2), img.shape[1] - int(lado_q), int(lado_q)):
        img = draw_angled_rec(random.randint((x + lado_q // 2) -
                                             emb, (x + lado_q // 2) + emb),
                              random.randint((y + lado_q // 2) -
                                             emb, (y + lado_q // 2) + emb),
                              lado_q, lado_q,
                              random.uniform(float(f"-{emb}"), emb),
                              img, 0, 4)
    emb += random.randint(0, 3)

Agora vamos salvar a imagem em um arquivo, para isso vamos utilizar a biblioteca “cv2”, da seguinte forma:

cv2.imwrite("art.png", img)

Conclusão

O código final completo está logo abaixo:

import math
import random

import cv2
import numpy as np

img = np.zeros((2340, 1080, 1), np.uint8)
img[:, :] = 255


def draw_angled_rec(x0, y0, width, height, angle, img, color, line_w):
    _angle = angle * math.pi / 180.0
    b = math.cos(_angle) * 0.5
    a = math.sin(_angle) * 0.5
    pt0 = (int(x0 - a * height - b * width),
           int(y0 + b * height - a * width))
    pt1 = (int(x0 + a * height - b * width),
           int(y0 - b * height - a * width))
    pt2 = (int(2 * x0 - pt0[0]), int(2 * y0 - pt0[1]))
    pt3 = (int(2 * x0 - pt1[0]), int(2 * y0 - pt1[1]))

    cv2.line(img, pt0, pt1, color, line_w, lineType=cv2.LINE_AA)
    cv2.line(img, pt1, pt2, color, line_w, lineType=cv2.LINE_AA)
    cv2.line(img, pt2, pt3, color, line_w, lineType=cv2.LINE_AA)
    cv2.line(img, pt3, pt0, color, line_w, lineType=cv2.LINE_AA)

    return img


lado_q = img.shape[1] / 13
emb = 0
for y in range(int(lado_q // 2), img.shape[0] - int(lado_q), int(lado_q)):
    for x in range(int(lado_q // 2), img.shape[1] - int(lado_q), int(lado_q)):
        img = draw_angled_rec(random.randint((x + lado_q // 2) - 
                                             emb, (x + lado_q // 2) + emb),
                              random.randint((y + lado_q // 2) -
                                             emb, (y + lado_q // 2) + emb),
                              lado_q, lado_q,
                              random.uniform(float(f"-{emb}"), emb),
                              img, 0, 4)
    emb += random.randint(0, 3)

cv2.imwrite("art.png", img)

Ao final da execução do código, a imagem gerada será salva em um arquivo chamado “art.png”, o resultado será semelhante( porém diferente) ao da imagem abaixo:

Resultado da imagem gerada

Obrigado por visitar o blog e por ler esse artigo, se tive qualquer dúvida, ideia ou sugestão, não hesite em entrar em contato pelo meu e-mail: lindomar@covildodev.com.br