Skip to content

Commit bcb64e5

Browse files
authored
v2.0.0 - detecção de capacete e reescrita do núcleo
1 parent 52f3f69 commit bcb64e5

50 files changed

Lines changed: 4320 additions & 2805 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,15 @@
1-
# Não há credenciais neste projeto.
2-
# Todas as configurações ficam em config.py
1+
# Todas as variaveis abaixo sao opcionais: o projeto roda sem nenhuma delas.
2+
# Para ajustes de limite e resolucao, prefira settings_local.json.
3+
4+
# Caminho para pesos proprios (padrao: yolov8n-pose.pt, baixado automaticamente)
5+
EPI_MODEL_PATH=
6+
7+
# Porta do servidor web
8+
EPI_PORT=5000
9+
10+
# Aceleracao OpenVINO em iGPU Intel. Exporte o modelo antes:
11+
# yolo export model=yolov8n-pose.pt format=openvino
12+
EPI_USE_OPENVINO=0
13+
14+
# Limite de threads do PyTorch. Vazio ou 0 deixa a decisao para o PyTorch.
15+
EPI_TORCH_THREADS=

.github/workflows/ci.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
8+
jobs:
9+
test:
10+
runs-on: ubuntu-latest
11+
strategy:
12+
matrix:
13+
python-version: ["3.10", "3.12"]
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- uses: actions/setup-python@v5
19+
with:
20+
python-version: ${{ matrix.python-version }}
21+
cache: pip
22+
23+
- name: Instalar dependencias
24+
run: |
25+
python -m pip install --upgrade pip
26+
pip install -r requirements-dev.txt
27+
28+
- name: Lint
29+
run: ruff check .
30+
31+
- name: Testes
32+
run: pytest -q

.gitignore

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,33 @@ __pycache__/
55
.ruff_cache/
66
.venv/
77
venv/
8+
env/
89

9-
# Segredos e configuracoes locais
10-
.env
11-
.env.*
12-
!.env.example
10+
# Configuracao especifica da maquina (cada instalacao tem a sua)
11+
settings_local.json
12+
13+
# Zonas desenhadas na interface web
14+
zones.json
15+
16+
# Pesos do modelo: baixados na primeira execucao, nao versionar
17+
*.pt
18+
*.onnx
19+
*.engine
20+
*_openvino_model/
21+
models/
22+
23+
# Saidas de processamento
24+
*_epi.mp4
25+
output/
26+
runs/
27+
28+
# Material de origem que pode conter pessoas ou ambientes privados
29+
input/
30+
raw/
1331

1432
# IDE e sistema operacional
1533
.vscode/
1634
.idea/
1735
.DS_Store
1836
Thumbs.db
19-
20-
# Resultados e logs
2137
*.log
22-
resultado*.mp4
23-
runs/
24-
25-
# Dataset local: pode conter imagens pessoais
26-
dataset_epi/raw/**
27-
28-
# Videos locais podem conter pessoas ou ambientes privados
29-
demo/*.mp4
30-
31-
# Pesos gerados durante treinamento
32-
*.pt
33-
models/.cache/
34-
models/*.onnx
35-
models/*.engine
36-
models/*.xml
37-
models/*.bin

CHANGELOG.md

Lines changed: 223 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,230 @@
11
# Changelog
22

3-
## [1.0.0] — 2026-08-21
3+
Formato baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/)
4+
e [Versionamento Semântico](https://semver.org/lang/pt-BR/).
5+
6+
---
7+
8+
## [2.0.0] — 2026-09
9+
10+
Reescrita do núcleo de detecção. A mudança que motiva a virada de versão
11+
maior é a **detecção de capacete**, que não existia na v1, mas o caminho
12+
até ela obrigou a repensar como as regiões de análise são delimitadas.
413

514
### Adicionado
6-
- Detecção de pessoas com YOLOv8n em tempo real
7-
- Análise HSV para coletes laranja e amarelo high-vis
8-
- Overlay profissional com Pillow (bounding boxes, labels, barra de status)
9-
- Suporte a webcam e vídeo demo via argparse
10-
- Captura threaded (sem blocking I/O)
11-
- Indicador de cobertura de colete por pessoa
12-
- Exportação de vídeo processado com `--save`
1315

14-
## [1.1.0] — 2026-08-21
16+
#### Detecção de capacete
17+
18+
A v1 monitorava apenas colete. Agora cada pessoa é avaliada em duas peças
19+
independentes, o que expõe a **violação parcial** — alguém de colete que
20+
subiu na empilhadeira sem capacete. Esse é o caso mais comum na prática e
21+
desaparecia num contador único de conformes.
22+
23+
Casar capacete por cor não funciona: eles existem em laranja, amarelo,
24+
branco, azul e vermelho, e qualquer boné da mesma cor viraria falso
25+
positivo. O que discrimina é a combinação de superfície lisa (plástico tem
26+
desvio padrão de brilho baixo, cabelo é texturizado), exclusão de pele e
27+
cabelo, e cobertura da calota dentro de máscara elíptica.
28+
29+
#### Recortes de análise ancorados em pose
30+
31+
Na v1 a faixa do tronco vinha de percentuais fixos da caixa. Agora os
32+
keypoints delimitam as regiões:
33+
34+
| Peça | Região | Keypoints |
35+
|---|---|---|
36+
| Colete | ombros até a bacia | 5, 6, 11, 12 |
37+
| Capacete | crânio até o meio da cabeça | derivado dos ombros |
38+
39+
O recorte por percentual fixo acabava medindo o piso do galpão em pessoas
40+
de perfil, e o efeito era um veredito baseado em pixels de concreto.
41+
42+
#### Esqueleto colorido por zona de equipamento
43+
44+
O esqueleto não é colorido por pessoa, e sim por peça de EPI: tronco e
45+
braços seguem o colete, cabeça e face seguem o capacete, pernas ficam
46+
neutras por não serem monitoradas. Isso torna a violação parcial legível
47+
sem depender de texto — tronco verde com cabeça vermelha diz "tem colete,
48+
falta capacete" numa olhada.
49+
50+
#### Rastreamento com identidade e duração
51+
52+
A v1 tinha estabilização temporal, mas sem identidade persistente. Agora
53+
cada pessoa mantém um identificador entre quadros, e disso vem o contador
54+
de **há quantos segundos** aquela pessoa está fora de conformidade — mais
55+
útil num painel de segurança do que o estado instantâneo.
56+
57+
#### Seleção automática de resolução
58+
59+
Descoberta que mais afeta o desempenho: não existe um valor bom de
60+
resolução para todo caso.
61+
62+
| Cena | Pessoas ocupam | Resolução | Observação |
63+
|---|---|---|---|
64+
| Plano aberto | ~20% da altura | 1280 | Em 896, duas pessoas desaparecem |
65+
| Plano fechado | ~60% da altura | 768 | Acerta **mais** que 1280 e roda 3x mais rápido |
66+
67+
O `render.py` mede o tamanho das pessoas em três quadros de amostra e
68+
decide. Em vídeo de plano fechado isso levou o processamento de 165s para
69+
55s, com detecção melhor.
70+
71+
#### Novo entrypoint `render.py`
72+
73+
Processa arquivo de vídeo e grava MP4 anotado, com opções de recorte
74+
(`--crop-top`, `--crop-bottom`) para remover relógio ou marca gravada no
75+
material de origem, e `--camera-name` para identificar a câmera ao juntar
76+
clipes.
77+
78+
#### Testes e integração contínua
79+
80+
15 testes cobrindo a geometria dos recortes e o comportamento do
81+
rastreador, incluindo os casos que a implementação errou durante o
82+
desenvolvimento: recorte degenerado em pessoa distante, keypoint facial
83+
incoerente, e leitura isolada que não deve derrubar o veredito. Workflow
84+
de CI rodando lint e testes em Python 3.10 e 3.12.
85+
86+
### Corrigido
87+
88+
#### Falso positivo de capacete por fundo claro
89+
90+
Medindo num retângulo em volta da cabeça, o teto claro do galpão entrava
91+
na conta e disparava a regra de capacete branco. Uma cabeça descoberta
92+
pontuava 0.595 — acima de qualquer limite razoável. A máscara elíptica
93+
restringe a medição à calota e o mesmo caso passou a pontuar 0.000.
94+
95+
#### Recorte da cabeça caindo fora da cabeça
96+
97+
O nariz e os olhos parecem a âncora natural para localizar a cabeça, mas
98+
de perfil ou de costas o modelo os estima em posição errada com confiança
99+
apenas marginal. O recorte saía da cabeça e caía no piso: o HSV médio
100+
dentro dele era cinza dessaturado, e a cobertura era zero antes de
101+
qualquer filtro.
102+
103+
A cabeça passou a ser derivada dos **ombros**, que chegam com confiança
104+
acima de 0.9 de forma consistente. Os pontos do rosto entram apenas como
105+
refinamento, aceito só quando é coerente com essa estimativa.
106+
107+
#### Escala vertical da cabeça
108+
109+
Uma tentativa intermediária derivava a altura da cabeça da sua largura.
110+
Quando a estimativa de largura errava, o recorte colapsava para 1 ou 2
111+
pixels de altura e a análise não tinha pixels para decidir. A escala passou
112+
a vir da distância **topo da caixa até o ombro**, que é medida direta.
113+
114+
#### Caixas duplicadas na mesma pessoa
115+
116+
Em plano fechado o detector às vezes devolve duas caixas para o mesmo
117+
operador. Descartar por sobreposição não resolve: duas caixas grandes e
118+
deslocadas ficam em IoU próximo de 0.44, abaixo de qualquer corte
119+
razoável. Passou a usar coincidência dos pontos faciais — se dois rostos
120+
estão no mesmo lugar, é a mesma pessoa.
121+
122+
#### Reflexo de piso detectado como pessoa
123+
124+
Com confiança baixa, reflexos no piso polido chegavam como caixas
125+
achatadas em proporções de 6:1. Filtro de proporção descarta, já que
126+
pessoa é sempre mais alta que larga, mesmo sentada.
127+
128+
#### Cobertura de colete subestimada
129+
130+
Material refletivo estoura a saturação de forma irregular sob luz
131+
fluorescente, criando buracos na máscara de cor. Operação morfológica de
132+
fechamento corrige.
133+
134+
#### Rótulo de EPI presente exibido em vermelho
135+
136+
A caixa fica vermelha quando falta qualquer peça, e os rótulos herdavam
137+
essa cor — então "COLETE OK" aparecia em vermelho e lia como falha. Cada
138+
peça passou a ter cor própria.
15139

16140
### Alterado
17-
- Pipeline assincrono para separar captura, inferencia e stream
18-
- Evidencia bilateral para rejeitar colete em apenas um braco
19-
- Estabilizacao temporal por pessoa
20-
- Ajustes de desempenho para CPU e stream de 20 FPS
21-
- Cor de ausencia/uso incorreto corrigida para vermelho em BGR
22-
- Documentacao e higienizacao de dados para publicacao
23-
- Estados explicitos de carregamento e erro no dashboard
24-
- Endpoint de saude e contrato formal da API
25-
- Testes automatizados, Ruff e CI com GitHub Actions
26-
- Licenca MIT e documentacao de limitacoes do modelo
141+
142+
#### Licença: MIT para AGPL-3.0
143+
144+
**Correção importante para quem usa a v1.** O projeto depende de
145+
Ultralytics YOLOv8, licenciado sob AGPL-3.0, que é uma licença viral —
146+
trabalhos derivados precisam manter a mesma licença. A v1 declarava MIT,
147+
o que é incompatível.
148+
149+
Se você pretende usar este código num produto de código fechado, precisa
150+
de licença comercial da Ultralytics. Isso valia igualmente para a v1; a
151+
mudança apenas torna a situação explícita.
152+
153+
#### Painel ao lado do vídeo, não sobre ele
154+
155+
O painel sobreposto custava 235 px da direita da imagem — faixa onde
156+
costuma cair marca de água ou relógio de câmera. A tela agora é ampliada
157+
e o quadro original aparece íntegro.
158+
159+
#### Nada é desenhado sobre o rosto
160+
161+
Uma versão intermediária marcava cabeça descoberta com uma retícula
162+
centrada nela, o que cobria exatamente o que o operador de câmera precisa
163+
ver. A marca de capacete ausente virou um arco acima da cabeça.
164+
165+
#### Espessura proporcional ao tamanho da pessoa
166+
167+
Linha de espessura fixa vira borrão em plano fechado e desaparece em plano
168+
aberto. Agora acompanha a altura da caixa.
169+
170+
#### Detector sem estado, histórico isolado no rastreador
171+
172+
`EPIDetector.detect()` não guarda histórico; todo acúmulo fica no
173+
`Tracker`. Isso permite testar a decisão de um quadro isolado e
174+
compartilhar um único detector entre várias câmeras.
175+
176+
#### Configuração local fora do repositório
177+
178+
Ajustes de máquina vão em `settings_local.json`, ignorado pelo git, em vez
179+
de editar `config.py`. Atualizar o projeto não sobrescreve mais o ajuste
180+
local.
181+
182+
#### Limites com a medição que os justifica
183+
184+
Cada valor em `config.py` traz em comentário o dado que o sustenta. O
185+
limite de capacete em 0.24, por exemplo: em vídeo real, cabeça com
186+
capacete pontua entre 0.29 e 0.82; cabeça descoberta pontua 0.00.
187+
188+
### Removido
189+
190+
- `core/display.py`, substituído por `core/overlay.py`, que só desenha e
191+
não toma decisão.
192+
- Identidade visual e configuração específicas de uma empresa. O projeto
193+
agora sobe genérico.
194+
- Duas implementações paralelas de detecção que conviviam no código.
195+
Todos os entrypoints usam a mesma base.
196+
197+
### Desempenho
198+
199+
Medido em CPU, sem placa dedicada, vídeo 1280×720:
200+
201+
| Resolução | Tempo por quadro | Taxa |
202+
|---|---|---|
203+
| 640 | 159 ms | 6.0 /s |
204+
| 768 | ~200 ms | 5.0 /s |
205+
| 960 | 285 ms | 3.4 /s |
206+
| 1280 | 519 ms | 1.9 /s |
207+
208+
O detector responde por cerca de 99% do custo por quadro. A análise de cor
209+
de colete e capacete gasta 5 ms somados — otimizar essa parte não muda
210+
nada, o que só ficou claro depois de medir.
211+
212+
---
213+
214+
## [1.0.0]
215+
216+
Primeira versão pública
217+
([Reinattos/EPI-Detect](https://github.com/Reinattos/EPI-Detect)).
218+
219+
### Adicionado
220+
221+
- Detecção de pessoas com YOLO Pose.
222+
- Identificação de colete refletivo por regras de cor HSV na região do
223+
tronco, com verificação de laranja e amarelo-limão.
224+
- Validação bilateral para reduzir falso positivo.
225+
- Estabilização temporal da classificação.
226+
- Pipeline assíncrono de captura e inferência.
227+
- Painel web com métricas e alertas.
228+
- Registro de eventos com marcação de horário.
229+
- API local para integração.
230+
- Suporte a webcam, arquivo de vídeo e vídeo de demonstração.

0 commit comments

Comments
 (0)