NodeJS - Ofuscando rostos humanos em fotos
Misael Braga de Bitencourt
Posted on October 26, 2023
Em 2017, eu criei um pequeno utilitário em NodeJS para ofuscar rostos de pessoas em fotos. Esse programa, quando executado, buscava todos os arquivos de imagens em um diretório informado e, depois disso, ofuscava (ou removia) todos os rostos de fotos. A integridade das fotos era preservada, apenas onde haviam as expressões faciais que eram removidas.
Esse se tornou um bom utilitário para não gravar um dado sensível do usuário que é o seu próprio rosto. Isso pode ser útil para ser utilizado em websites onde deve-se preservar a identidade de crianças ou pessoas que não queriam ser expostas.
Nas últimas semanas, eu estive refatorando esse projeto que antes estava escrito em javascript e agora foi refatorado para Typescript. Aproveitei o tempo para escrever esse texto demonstrando como é simples de se implementar essa solução utilizando algumas bibliotecas.
Gloob + OpenCV4NodeJs + JIMP
Essas três libs podem ser combinadas para gerar a solução proposta.
Gloob
O Gloob é a mais conhecida e utilizada das três. Ela é utilizada para procurar arquivos utilizando comandos semelhantes aos de um shell, sendo que a mesma implementação funciona em todas as plataformas. Exemplo:
const filesStream = await glob('**/*.dat')
O código acima pesquisará em todos os diretórios, a partir do contexto, os arquivos ".dat" e retornará uma lista com eles.
OpenCV4NodeJs
A biblioteca de I.A. OpenCV4NodeJs oferece uma interface para chamar as rotinas do OpenCV no NodeJS.
JIMP
Esta última é uma utilizada para a manipulação de imagens em Javascript no NodeJS. O JIMP é utilizado para aplicar o efeito de blur na região onde existem os rostos.
Apesar de, nesse exemplo, a utilizarmos apenas para esse fim, o JIMP é uma poderosa ferramenta no estilo image magick nos permite realizar vários tipos de manipulação de imagem. Segue abaixo um exemplo de como redimensionar uma foto, alterar sua qualidade e sua paleta de cores em algumas linhas:
Jimp.read("lenna.png", (err, lenna) => {
if (err) throw err;
lenna
.resize(256, 256) // resize
.quality(60) // set JPEG quality
.greyscale() // set greyscale
.write("lena-small-bw.jpg"); // save
});
Juntando as peças
As partes mais dificultosas da resolução do problema foram feitas pelas bibliotecas anteriormente citadas. Agora, só nos resta juntá-las para criarmos a solução.
- O Gloob vai varrer o diretório contendo as imagens;
- O OpenCV vai verificar onde exitem rostos em cada uma;
- O JIMP vai aplicar o blur na região onde existem rostos;
index.ts
(async () => {
await argParser.parseArgs(process.argv);
const rootDir = argParser.getArgument('dir');
const files = await getImageFiles(rootDir || '');
console.log(rootDir, files);
await imageService.detectFaceInFiles(files);
console.log('Done!');
})().catch(err => {
console.error(err);
})
image-service.ts
import * as cv from "opencv4nodejs";
import * as jimp from "jimp";
export type Rect = {
x: number;
y: number;
width: number;
height: number;
};
export default {
readImage(path: string) {
return cv.imreadAsync(path).then((img: any) => img.bgrToGrayAsync());
},
async hideFromImage(file: string, regions: Rect[]) {
regions = regions.slice();
let image = await jimp.read(file);
for (let region of regions) {
image = await image.pixelate(21, region.x, region.y, region.width, region.height);
}
return image;
},
async detectFaceInFiles(files: string[]) {
const classifier = new cv.CascadeClassifier(cv.HAAR_FRONTALFACE_ALT2);
const result = [];
for (let file of files) {
try {
const grayImg = await this.readImage(file);
const res = await classifier.detectMultiScaleAsync(grayImg);
result.push(res);
console.log(res);
const img = await this.hideFromImage(file, res.objects);
await img.write(file);
} catch (e) {
console.error(e);
}
}
return result;
}
}
**obs: o código acima pode ser refatorado para executar as tarefas em paralelo mas isso pode demandar mais recursos computacionais.
O código completo pode ser acessado em: https://github.com/misabitencourt/face-blur.
Posted on October 26, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.