Algumas semanas atrás minha esposa pediu ajuda para encontrar o último erro do jogo dos 6 erros na atividade escolar do nosso filho (e eram 6 erros, mesmo, não 7). Ela já tinha ajudado nosso filho a encontrar 5 erros. Depois de quase 10 minutos encarando aquelas imagens, virando o livro de todos os jeitos, olhando inclinado, também não consegui encontrar e a tarefa voltou para escola faltando a indicação da última diferença.
Alguns dias depois lembrei disso e percebi que poderia resolver o problema usando programação de computadores.
Dentro do computador, uma imagem é um tabuleiro com pontinhos muito pequenos. Cada potinho tem uma cor e a composição de milhares deles forma a imagem. E para o computador, uma cor é apenas um número. E se as coisas são números, podemos fazer um menos o outro e encontrar a diferença, tão simples quanto 9 menos 7 é igual a 2.
As cores
Dentro do computador, todas as cores que vemos são uma composição de 3 cores, que são chamadas de cores primárias: vermelho, verde e azul. A mistura dessas cores forma qualquer outra cor, bastando colocar um pouco mais ou um pouco menos delas. Misturar em quantidades iguais o vermelho com verde vai formar o amarelo, e um pouco menos de verde na mistura vai formar uma cor alaranjada.
Por se tratar de um computador, onde as coisas são sempre representadas por números, cada uma dessas 3 cores primárias recebe um número para indicar sua intensidade. Esse valor varia de 0 até 255, indo de 1 em 1. Portanto, a combinação dessas cores pode gerar mais de 16 milhões de outras cores. Para comparar, estima-se que o olho humano seja capaz de distinguir até 1 milhão de cores diferentes. Nesse sistema, se misturarmos vermelho, verde e azul na intensidade máxima, isto é, cada cor com valor igual a 255, teremos a cor branca. O contrário, quando elas estão com valores 0, temos a cor preta.
Talvez você esteja pensando que misturar tintas, dessas de pintar parede, na cor vermelha, verde e azul, não vai sair a cor branca. E se for esse o caso, você está certo! Acontece que a cor da tinta é um pigmento, e dentro do computador, a cor é tratada como luz. Tintas de impressora são outros exemplos de pigmentos. Nessas máquinas, as cores primárias são ciano, magenta e amarelo, e o preto é utilizado para adicionar contraste. E não é possível criar a cor branca a partir delas. Geralmente, a cor branca é o próprio papel que está sendo utilizado na impressão.
Resolvendo o problema
Então, sabendo que as cores no computador são números, podemos fazer operações matemáticas simples com elas. E dentro do contexto de imagens, existem muitas possibilidades. Inclusive, o processamento de imagens é uma área de atuação com muita pesquisa dentro da ciência da computação e outras atividades ligadas à tecnologia. Aplicativos como Instagram, com seus infinitos filtros, também fazem muito processamento de imagem, seja uma fotografia ou um vídeo.
No problema do jogo dos 6 erros do meu filho, fiz um scanner da página do livro, então recortei as imagem de modo que ficassem do mesmo tamanho e finalizei com algumas linhas de código de programação para calcular a diferença entre as duas imagens.
Passo a passo
Para fazer o scanner, usei minha antiga impressora. Também é possível usar a câmera do celular, com muito cuidado para que a imagem não fique inclinadas.
Depois, eu recortei e deixei as imagens com o mesmo tamanho. Fiz isso usando o programa paint.net (https://www.getpaint.net/) para Windows.
Então criei um programa para ler as duas imagens e comparar elas ponto a ponto, colocando o resultado em uma terceira imagem. Primeiramente, esse programa vai converter cada imagem em outra construída com com níveis de cinza. É bem mais fácil fazer uma comparação com níveis de cinza porque não temos que tratar com cada uma das cores primárias separadamente. Quando a imagem está em níveis de cinza, a intensidade mais forte, 255, é o branco, e a menor, 0, é o preto, e todos os outros valores são variações de cinza.
Depois, a lógica é a seguinte, se a diferença da tonalidade de cinza entre um par de pontos for menor que 20 unidades, significa que as cores são bem próximas, isto é, o tom de cinza entre elas não varia muito. Na imagem resultante, eu marquei esse local com a cor preta se a diferença for maior que 20, caso contrário, vai ficar com uma cor mais clara. Esse valor 20 eu obtive depois de alguns testes. Com um valor muito baixo, o programa vai considerar diferenças muito sutis, e com um valor muito alto, quase nada não vai ter diferença. Isso para mim era um problema, pois meu scaner gerava uma imagem com muitas diferenças bem sutis, que não são nem perceptíveis a olho nu, uma vez que conseguimos ver 1 milhão de cores, e a imagem no computador pode ter mais de 16 milhões de cores.
O programa que escrevi é bastante simples. Utilizei somente a API padrão do Java. Para fazer a conversão do vermelho, verde e azul em cinza, utilizei uma formula que encontrei na internet (link nas referências). Resumidamente, o nível de cinza resultante vai ser uma combinação de 29.9% do vermelho original, mais 58.7% do verde original e mais 11.4% do azul original.
import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.nio.file.Path;
import java.nio.file.Paths;
public class Main {
public static void main(String[] args) throws Exception {
Path dir = Paths.get("caminho para as imagens");
BufferedImage imageA = ImageIO.read(dir.resolve("imagem_a.png").toFile());
BufferedImage imageB = ImageIO.read(dir.resolve("imagem_b.png").toFile());
BufferedImage differences = new BufferedImage(
imageA.getWidth(null),
imageA.getHeight(null),
BufferedImage.TYPE_INT_RGB
);
for (int i = 0; i < imageA.getWidth(null); i++) {
for (int j = 0; j < imageA.getHeight(null); j++) {
Color a = new Color(imageA.getRGB(i, j));
Color b = new Color(imageB.getRGB(i, j));
double aGray = a.getRed() * 0.299 + a.getGreen() * 0.587 + a.getBlue() * 0.144;
double bGray = b.getRed() * 0.299 + b.getGreen() * 0.587 + b.getBlue() * 0.144;
int n = normalize((int) Math.abs(aGray - bGray));
differences.setRGB(i, j, new Color(n, n, n).getRGB());
}
}
ImageIO.write(differences, "png", dir.resolve("diff.png").
toFile());
}
private static int normalize(int colorDiff) {
if (colorDiff < 20) return 200;
else return 0;
}
}
Finalmente, pude encontrar com bastante facilidade a última diferença que faltava na atividade do meu filho.
Também fiz um outro teste com uma imagem que peguei na internet (essa sim, com 7 erros):
Conclusões
Isso é uma demonstração de como programação de computadores pode ser utilizada para resolver problemas do dia a dia. Programação pode ser usada como passatempo, como brincadeira, como ferramenta de aprendizado. Na verdade, quase tudo que fazemos no dia a dia tem programação por trás, da geladeira ao celular, do micro-ondas ao aplicativo de mensagem. Nós usamos coisas programadas e nem percebemos, tamanha é inserção, abrangência e naturalidade com que elas resolvem nossos problemas.
Jornalistas, cada vez mais, estão aprendendo a programar para automatizar a busca por informações. Eles fazem programas em Python, por exemplo, que conseguem vasculhar sites públicos do governo em busca de dados para embasar alguma matéria.
As possibilidades da programação de computadores são do tamanho da imaginação.
Referências
Cor: luz ou pigmento?: http://www.invivo.fiocruz.br/cienciaetecnologia/cor-luz-ou-pigmento/
Example: Grayscale and Color in Images: http://support.ptc.com/help/mathcad/en/index.html#page/PTC_Mathcad_Help/example_grayscale_and_color_in_images.html
Existem cores que não conseguimos ver: https://rfm.sapo.pt/content/7144/existem-cores-que-nao-conseguimos-ver
Folha de São Paulo vai realizar treinamento em jornalismo de dados: https://paulovasconcellos.com.br/folha-de-s%C3%A3o-paulo-vai-realizar-treinamento-em-jornalismo-de-dados-e80ef7500128