|
1 | 1 | # Changelog |
2 | 2 |
|
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. |
4 | 13 |
|
5 | 14 | ### 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` |
13 | 15 |
|
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. |
15 | 139 |
|
16 | 140 | ### 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