7.7. Normalização#
ml.Model.predict() aceita uma lista de entradas porque algumas redes têm mais de um tensor de entrada, mas a lista não tem forma de transportar argumentos por entrada em linha – não existe nenhum slot kwarg para «recortar esta entrada a (x, y, w, h) mas deixar as outras entradas em paz». ml.preprocessing.Normalization é o wrapper que preenche essa lacuna. Uma instância de Normalization contém os parâmetros para uma entrada; o script passa a entrada envolvida na lista do predict sempre que necessita de algo diferente dos valores predefinidos.
A razão mais comum para o utilizar é recortar uma região específica do fotograma 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 fotograma de origem a recortar antes do redimensionamento. Por omissão, corresponde ao fotograma inteiro. A maioria das utilizações deNormalizationdefine apenas este parâmetro.scale– o intervalo(min, max)que os tensores de entrada em ponto flutuante esperam após a normalização. O intervalo de pixel0..255é mapeado linearmente para este intervalo. 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(R, G, B)por canal subtraída à imagem após o escalonamento. Corresponde às estatísticas de canal utilizadas no treino da rede –(0.485, 0.456, 0.406)para redes derivadas do ImageNet é o exemplo canónico. As redes em escala de cinzentos reduzem a média para um valor de luminância usando o padrão0.299*R + 0.587*G + 0.114*B.stdev– desvio padrão(R, G, B)por canal pelo qual a imagem é dividida após a subtração da média, correspondendo igualmente às estatísticas de treino da rede. Reduzido para luminância da mesma forma para redes em escala de cinzentos.
7.7.2. Quando os parâmetros são relevantes#
scale, mean e stdev são ignorados quando o input_dtype da rede é int8 ou uint8. Para redes com entrada inteira, os bytes da imagem recortada são escritos diretamente no tensor e o input_scale e o input_zero_point da rede tratam da conversão de inteiro para real. Os três parâmetros só são relevantes quando a rede espera entrada em ponto flutuante.
roi é lido em todos os casos – controla qual a parte do fotograma de origem que chega à rede, independentemente do dtype de entrada.
7.7.3. ROI e redimensionamento#
O ROI é escalado bilinearmente das suas dimensões de origem para as dimensões de entrada da rede. A imagem é centrada no destino e o escalonamento preenche o destino – não preserva a proporção da imagem. Um ROI não quadrado alimentado a uma entrada de rede quadrada fica esticado horizontal ou verticalmente.
Se o esticamento é relevante depende da rede. Os modelos de deteção facial e de pontos de referência como a família MediaPipe (BlazeFace, FaceLandmarks, HandLandmarks, MoveNet) foram treinados com recortes quadrados e degradam rapidamente quando a proporção da entrada está incorreta; para estes, a aplicação precisa de lhes fornecer um ROI quadrado – seja capturando numa resolução quadrada através de window() ou recortando com o parâmetro roi=. Os detetores de objetos da família YOLO são tipicamente treinados com aumentação que inclui esticamentos aleatórios e aceitam ROIs não quadrados sem grande perda de precisão; passar o fotograma capturado inteiro diretamente costuma funcionar bem.
Quando as dimensões de entrada da rede correspondem exatamente ao ROI, o escalonamento colapsa para uma cópia, que é o caso mais económico.
7.7.4. Substituir o valor predefinido#
predict() envolve automaticamente cada entrada image.Image com Normalization() – os parâmetros predefinidos acima. A maioria dos modelos que acompanham a câmara foram treinados contra intervalos de pixel que os valores predefinidos já cobrem, pelo que o caso comum é passar a imagem diretamente:
result = model.predict([img])
Para utilizar um ROI personalizado – a substituição mais comum – construa uma Normalization com o ROI definido e associe a imagem a ela:
from ml.preprocessing import Normalization
norm = Normalization(roi=(80, 60, 160, 120))
result = model.predict([norm(img)])
Para corresponder às estatísticas de canal em tempo de treino de uma rede, defina os parâmetros em 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 Normalization sobre a imagem devolve uma nova instância associada a partir da qual o motor preenche o tensor. A instância associada é o que o predict aceita em vez da imagem bruta e, como é um objeto por entrada, uma rede com múltiplas entradas pode misturar imagens com ROIs diferentes na mesma lista do predict.
Para redes que esperam entradas que a aplicação já produziu em forma de tensor – um buffer de um periférico, um ndarray calculado por outra pipeline, dados numéricos não-imagem – ignore Normalization inteiramente e passe o ndarray ou um callable que o produza. predict() passa esses diretamente para o motor sem os envolver.