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.