7.7. Нормализация¶
ml.Model.predict() принимает список входов, потому что у некоторых сетей более одного входного тензора, но список не позволяет встроенно передавать аргументы для отдельных входов – нет места для именованного аргумента вроде «обрезать этот вход до (x, y, w, h), но оставить остальные входы как есть». ml.preprocessing.Normalization – это обёртка, заполняющая этот пробел. Экземпляр Normalization содержит параметры для одного входа; скрипт передаёт обёрнутый вход в список predict всякий раз, когда ему нужно что-то отличное от значений по умолчанию.
Самая частая причина к ней обратиться – обрезать определённую область захваченного кадра для сети вместо всего изображения.
7.7.1. Параметры¶
Normalization(scale=(0.0, 1.0),
mean=(0.0, 0.0, 0.0),
stdev=(1.0, 1.0, 1.0),
roi=None)
roi– прямоугольник(x, y, w, h)в исходном кадре, который обрезается перед масштабированием. По умолчанию – весь кадр. В большинстве случаевNormalizationзадаёт только этот параметр.scale– диапазон(min, max), который входные тензоры с плавающей точкой ожидают после нормализации. Диапазон пикселей0..255линейно отображается в этот диапазон. Распространённые значения –(0.0, 1.0)для сетей, обученных с ReLU, и(-1.0, 1.0)для симметрично нормализованных сетей.mean– среднее по каждому каналу(R, G, B), вычитаемое из изображения после масштабирования. Соответствует статистике каналов, на которой обучалась сеть –(0.485, 0.456, 0.406)для сетей на основе ImageNet – канонический пример. Сети в оттенках серого приводят среднее к значению яркости по стандартной формуле0.299*R + 0.587*G + 0.114*B.stdev– стандартное отклонение по каждому каналу(R, G, B), на которое изображение делится после вычитания среднего, опять же в соответствии со статистикой обучения сети. Для сетей в оттенках серого приводится к яркости тем же способом.
7.7.2. Когда параметры важны¶
scale, mean и stdev игнорируются, когда input_dtype сети равен int8 или uint8. Для сетей с целочисленным входом байты обрезанного изображения записываются в тензор напрямую, а собственные input_scale и input_zero_point сети выполняют преобразование из целого числа в действительное. Эти три параметра важны только тогда, когда сеть ожидает вход с плавающей точкой.
roi читается в любом случае – он определяет, какая часть исходного кадра достигает сети, независимо от входного dtype.
7.7.3. ROI и масштабирование¶
ROI билинейно масштабируется из своих исходных размеров до входных размеров сети. Изображение центрируется в назначении, и масштабирование заполняет назначение – оно не сохраняет соотношение сторон. Неквадратный ROI, поданный на квадратный вход сети, выходит растянутым по горизонтали или вертикали.
Имеет ли значение растяжение, зависит от сети. Модели обнаружения лиц и ориентиров вроде семейства MediaPipe (BlazeFace, FaceLandmarks, HandLandmarks, MoveNet) обучались на квадратных обрезках и быстро деградируют, когда соотношение сторон входа неверно; для них приложению нужно подавать квадратный ROI – либо захватывая в квадратном размере кадра через window(), либо обрезая с помощью параметра roi=. Детекторы объектов семейства YOLO обычно обучаются с аугментацией, включающей случайные растяжения, и принимают неквадратные ROI без значительной потери точности; передача всего захваченного кадра напрямую обычно подходит.
Когда входные размеры сети точно совпадают с ROI, масштабирование сводится к копированию, что является самым дешёвым случаем.
7.7.4. Переопределение значения по умолчанию¶
predict() автоматически оборачивает каждый вход image.Image с помощью Normalization() – с параметрами по умолчанию, описанными выше. Большинство моделей, поставляемых с камерой, обучались на диапазонах пикселей, которые значения по умолчанию уже покрывают, поэтому распространённый случай – передать изображение напрямую:
result = model.predict([img])
Чтобы использовать пользовательский ROI – самое распространённое переопределение – создайте Normalization с заданным ROI и привяжите к нему изображение:
from ml.preprocessing import Normalization
norm = Normalization(roi=(80, 60, 160, 120))
result = model.predict([norm(img)])
Чтобы соответствовать статистике каналов на этапе обучения сети, задайте параметры с плавающей точкой:
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)])
Вызов экземпляра Normalization на изображении возвращает новый привязанный экземпляр, из которого движок заполняет тензор. Привязанный экземпляр – это то, что predict принимает вместо сырого изображения, и поскольку это объект для отдельного входа, многовходовая сеть может смешивать изображения с разными ROI в одном списке predict.
Для сетей, ожидающих входы, которые приложение уже сформировало в виде тензора – буфер от периферийного устройства, ndarray, вычисленный другим конвейером, нечисловые данные не изображения – полностью пропустите Normalization и передайте ndarray или вызываемый объект, который его формирует. predict() передаёт их движку без обёртывания.