ASCII e GO
Depois que terminei de montar o site, mandei o link pra um amigo (salve, Leandro) pra saber a opinião dele. Aí ele mandou: “Já faz um projeto em go, Img -> ascii no cli” Gostei da ideia, larguei o que estava fazendo pra estudar como fazer isso. Ultimamente, sinto que estou meio “destreinado” por usar muito LLM, então resolvi encarar esse projeto com o mínimo de ajuda possível. É um treino pra cabeça, mesmo que a frustração às vezes bata forte.
Pesquisa e Referências
Pesquisando no YouTube alguma ideia de como começar do zero, encontrei um vídeo (aquele que fica na lista de “assistir depois” e nunca é visto) do Daniel Shiffman. No vídeo, ele explica como transformar pixels em caracteres ASCII, falando do básico: pegar um pixel, entender seu brilho e montar uma arte legal com isso. Ele ainda cita um ponto muito interessante que acredito que se aplique a toda computação. Todo esse lance não é um “desafio” , mas uma “jornada , pra aprender sem pressão e poder se divertir no processo.
Também pesquisei sobre a escala de cinza (greyscale), porque no terminal o ASCII é todo em preto e branco. Encontrei um site bem antigo, de 97, do Paul Bourke, que explica em uma página como isso funciona.
Estrutura do Código
O código final ficou mais ou menos assim:
func NewImageProcessor(image image.Image) *ImageProcessor {
return &ImageProcessor{img: image}
}
Essa função cria o processador de imagens, que vai cuidar de tudo.
func (ip *ImageProcessor) CalculateBlockAverage(startX, startY int) int {
var sum, count int
bounds := ip.img.Bounds()
maxX, maxY := bounds.Max.X, bounds.Max.Y
for x := startX; x < startX+blockWidth && x < maxX; x++ {
for y := startY; y < startY+blockHeight && y < maxY; y++ {
sum += grayscale(ip.img.At(x, y))
count++
}
}
if count == 0 {
return 0
}
return sum / count
}
```>
Aqui, o programa pega um pedacinho da imagem e calcula o brilho médio dele, convertendo cada pixel para cinza.
##
```go
func (ip *ImageProcessor) Generate() string {
var output string
bounds := ip.img.Bounds()
max := bounds.Max
for y := 0; y < max.Y; y += verticalStep {
for x := 0; x < max.X; x += horizontalStep {
avg := ip.CalculateBlockAverage(x, y)
output += mapBrightnessToChar(avg)
}
output += "\n"
}
return output
}
Essa parte transforma cada bloco de pixels em um caractere ASCII, juntando tudo e criando a arte.
func loadImage(filePath string) (image.Image, error) {
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
return img, err
}
Aqui, o programa abre o arquivo da imagem e decodifica ela para poder processar.
func grayscale(c color.Color) int {
r, g, b, _ := c.RGBA()
return int(0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b))
}
Essa função pega uma cor e calcula seu valor em cinza, usando uma conta simples.
func mapBrightnessToChar(avgBrightness int) string {
index := len(asciiRamp) * avgBrightness / 65536
return string(asciiRamp[index])
}
Aqui, o brilho médio é convertido num caractere, de acordo com uma lista de caracteres que vai do mais escuro ao mais claro.
func main() {
img, err := loadImage("images.png")
if err != nil {
panic(err)
}
processor := NewImageProcessor(img)
fmt.Print(processor.Generate())
}
No final, o main junta tudo: carrega a imagem, processa e imprime a arte ASCII no terminal.
No fim das contas, ficou um projeto bem legal e com várias chances de melhorar. Comecei a escrever esse texto, mas acabei parando até o Leandro me cobrar de novo pra postar. Enfim, resolvi deixar tudo registrado.
caso queira conferir o repositório, o projeto se chama ascii-go.
Fora isso… Obrgiado pela atenção, um abraço e até mais!
j