7.7. Normalização¶
ml.Model.predict() recebe uma lista de entradas porque algumas redes têm mais de um tensor de entrada, mas a lista não tem como carregar argumentos por entrada de forma inline – não há espaço de kwarg para “recorte esta entrada para (x, y, w, h) mas deixe as outras entradas intactas”. ml.preprocessing.Normalization é o invólucro que preenche essa lacuna. Uma instância de Normalization armazena os parâmetros para uma entrada; o script passa a entrada encapsulada na lista de predict sempre que precisar de algo diferente dos padrões.
O motivo mais comum para recorrer a ela é recortar uma região específica do quadro capturado para a rede em vez da imagem inteira.
7.7.1. Parâmetros¶
Normalization(scale=(0.0, 1.0),
mean=(0.0, 0.0, 0.0),
stdev=(1.0, 1.0, 1.0),
roi=None)
roi– retângulo(x, y, w, h)no quadro de origem a ser recortado antes do redimensionamento. O padrão é o quadro inteiro. A maioria dos usos deNormalizationdefine apenas este parâmetro.scale– a faixa(min, max)que os tensores de entrada em ponto flutuante esperam após a normalização. A faixa de pixels0..255é mapeada linearmente para essa faixa. Valores comuns são(0.0, 1.0)para redes treinadas com ReLU e(-1.0, 1.0)para redes normalizadas simetricamente.mean– média por canal(R, G, B)subtraída da imagem após a escala. Corresponde às estatísticas de canal contra as quais a rede foi treinada –(0.485, 0.456, 0.406)para redes derivadas do ImageNet é o exemplo canônico. Redes em escala de cinza reduzem a média a um valor de luma usando o padrão0.299*R + 0.587*G + 0.114*B.stdev– desvio padrão por canal(R, G, B)pelo qual a imagem é dividida após a subtração da média, novamente correspondendo às estatísticas de treinamento da rede. Reduzido a luma da mesma forma para redes em escala de cinza.
7.7.2. Quando os parâmetros importam¶
scale, mean e stdev são ignorados quando o input_dtype da rede é int8 ou uint8. Para redes de entrada inteira, os bytes da imagem recortada são escritos diretamente no tensor e os próprios input_scale e input_zero_point da rede tratam a conversão de inteiro para real. Os três parâmetros importam apenas quando a rede espera entrada em ponto flutuante.
roi é lido em todos os casos – ele controla qual parte do quadro de origem chega à rede independentemente do dtype de entrada.
7.7.3. ROI e redimensionamento¶
A ROI é escalada bilinearmente de suas dimensões de origem para as dimensões de entrada da rede. A imagem é centralizada no destino e a escala preenche o destino – ela não preserva a proporção de aspecto. Uma ROI não quadrada alimentada a uma entrada de rede quadrada sai esticada horizontal ou verticalmente.
Se o esticamento importa depende da rede. Modelos de detecção de faces e de marcos como a família MediaPipe (BlazeFace, FaceLandmarks, HandLandmarks, MoveNet) foram treinados contra recortes quadrados e degradam rapidamente quando a proporção de aspecto da entrada está fora; para esses, a aplicação precisa fornecer uma ROI quadrada – seja capturando em um framesize quadrado por meio de window() ou recortando com o parâmetro roi=. Detectores de objetos da família YOLO normalmente são treinados com augmentação que inclui esticamentos aleatórios e aceitam ROIs não quadradas sem muita perda de acurácia; passar o quadro capturado inteiro diretamente geralmente é suficiente.
Quando as dimensões de entrada da rede correspondem exatamente à ROI, a escala se reduz a uma cópia, que é o caso mais barato.
7.7.4. Sobrescrevendo o padrão¶
predict() encapsula cada entrada image.Image com Normalization() automaticamente – os parâmetros padrão acima. A maioria dos modelos que acompanham a câmera foi treinada contra faixas de pixels que os padrões já cobrem, então o caso comum é passar a imagem diretamente:
result = model.predict([img])
Para usar uma ROI personalizada – a sobrescrita mais comum – construa um Normalization com a ROI definida e vincule a imagem a ele:
from ml.preprocessing import Normalization
norm = Normalization(roi=(80, 60, 160, 120))
result = model.predict([norm(img)])
Para corresponder às estatísticas de canal de treinamento de uma rede, defina os parâmetros de ponto flutuante:
norm = Normalization(scale=(0.0, 1.0),
mean=(0.485, 0.456, 0.406),
stdev=(0.229, 0.224, 0.225))
result = model.predict([norm(img)])
Chamar a instância de Normalization sobre a imagem retorna uma nova instância vinculada a partir da qual o motor preenche o tensor. A instância vinculada é o que o predict aceita no lugar da imagem bruta e, por ser um objeto por entrada, uma rede de múltiplas entradas pode misturar imagens com ROIs diferentes na mesma lista de predict.
Para redes que esperam entradas que a aplicação já produziu em forma de tensor – um buffer de um periférico, um ndarray computado por outra pipeline, dados numéricos que não são imagem – pule Normalization inteiramente e passe o ndarray ou um chamável que o produza. predict() os repassa ao motor sem encapsulá-los.