NodeJS - Ofuscando rostos humanos em fotos
Tags: node, javascript, typescript, ia
Published: 2023-10-26 21:20:51

---
title: NodeJS - Ofuscando rostos humanos em fotos
published: true
description: Como criar um utilitário para remover faces humanas de fotos em algumas linhas em Typescript + NodeJS.
tags: nodejs, javascript, typescript, ia
# cover_image: https://github.com/misabitencourt/face-blur/blob/master/docs/sample.png?raw=true
# Use a ratio of 100:42 for best results.
# published_at: 2023-10-26 21:16 +0000
---

![Face Blur](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/dvr7xeu5wffp99g1vpyd.png)

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](https://www.npmjs.com/package/glob) é 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:

```javascript
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](https://www.npmjs.com/package/opencv4nodejs) oferece uma interface para chamar as rotinas do [OpenCV](https://opencv.org/) no NodeJS.

![Face recognition](https://github.com/misabitencourt/face-blur/blob/master/docs/opencv.jpg?raw=true)

### JIMP

Esta última é uma utilizada para a manipulação de imagens em Javascript no NodeJS. O [JIMP](https://www.npmjs.com/package/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:

```javascript
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
```typescript
(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
```typescript
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](https://github.com/misabitencourt/face-blur).