Компьютерное зрение

Цифровое изображение: пиксели и цвет

Предварительные знания

  • Базовый Python: переменные, индексация, срезы списков
  • Целые числа и двоичная система: что такое байт и диапазон 0-255
  • Двумерные таблицы (матрицы) как сетка строк и столбцов

Первое цифровое изображение

В 1957 году Russell Kirsch и его команда в US National Bureau of Standards использовали компьютер SEAC, чтобы отсканировать фотографию его маленького сына в сетку 176x176 из бинарных значений. Этот квадрат из 30 976 пикселей стал первым цифровым изображением в истории и задал соглашение, которым мы пользуемся до сих пор: изображение это прямоугольный массив чисел. Двенадцать лет спустя, в 1969 году, Willard Boyle и George Smith в Bell Labs изобрели прибор с зарядовой связью (CCD), сенсор, который электронно преобразует свет в эти числа. CCD сделал возможными цифровые камеры и принёс Boyle и Smith Нобелевскую премию по физике 2009 года. Цветные изображения, с которыми мы работаем, объединяют эту идею с моделью RGB, храня три значения интенсивности на пиксель для красного, зелёного и синего.

2021 год. Tesla убирает радар. Все аналитики: «это безумие, автомобили ослепнут». Маск: «у людей нет радара - только глаза. Зачем нам то, чего нет у природы?» Vision-only Autopilot обучается на 100 петабайтах видео с 5 миллионов машин. Параллельно: ImageNet 2012 - AlexNet срезает ошибку с 26% до 15% за одну ночь. Hubel и Wiesel в 1959 году изучали нейроны кошки - и открыли принцип, на котором работают все свёрточные сети. Точка невозврата.

  • **Tesla Vision** (5M автомобилей) - vision-only система принимает решения о вождении в реальном времени, анализируя 8 камер с разрешением до 1280x960
  • **Face ID** - 30 000 инфракрасных точек строят 3D-карту лица за 50 миллисекунд: три канала IR-данных, точность 1/1 000 000
  • **Stanford AI + рентген** (2017) - нейросеть ставит диагноз рак кожи точнее среднего дерматолога; рентгеновский снимок - это просто grayscale-массив с 16-битными значениями
  • **Waymo 360°** - лидар + камеры строят карту мира в реальном времени; каждый кадр с восьми камер обрабатывается за миллисекунды

Пиксели - атомы изображения

2021 год. Tesla убирает радар из всех своих автомобилей. Аналитики в панике: «они ослепнут». Маск: «у людей нет радара - только глаза. Зачем нам то, чего нет у природы?» Vision-only Autopilot обучается на 100 петабайтах видео с 5 миллионов машин. Параллельно вспоминают 2012: ImageNet, трое студентов в Торонто, AlexNet срезает ошибку с 26% до 15% за одну ночь. Точка невозврата. Всё началось с одного понимания: **изображение - это просто таблица чисел**.

Каждое цифровое изображение - **двумерная таблица чисел**. Одна ячейка - один пиксель (от *picture element*). Для grayscale-изображения каждый пиксель хранит одно число от 0 (чёрный) до 255 (белый). Рентген в больнице, кадр с камеры Tesla, скан отпечатка пальца Face ID - математически одно и то же.

Почему 0-255? Один байт = 8 бит = 2⁸ = 256 возможных значений. Достаточно, чтобы человеческий глаз не замечал переходов между соседними оттенками.

В NumPy (и OpenCV) изображение - обычный `ndarray`. Координаты идут в порядке **(y, x)**, а не (x, y), как в математике. Первая ось - строки (высота), вторая - столбцы (ширина).

**Классическая ловушка:** `img[x, y]` вместо `img[y, x]`. В NumPy первый индекс - строка (вертикаль), второй - столбец (горизонталь). Перепутаете - изображение отразится по диагонали. Эта ошибка встречается даже в production-коде.

Чтобы быстро посмотреть изображение без GUI, используйте `cv2.imwrite('debug.png', img)` и откройте файл. Для Jupyter Notebook - `from matplotlib import pyplot as plt; plt.imshow(img, cmap='gray'); plt.show()`.

Изображение имеет shape (480, 640). Какой пиксель находится в правом нижнем углу?

Цветовые пространства: RGB, BGR, HSV

Один пиксель grayscale - одно число. Как хранить цвет? Нужна **модель**, описывающая цвет через несколько компонент. Самая известная - **RGB**: каждый цвет складывается из красного (Red), зелёного (Green) и синего (Blue). Face ID на iPhone снимает 30 000 инфракрасных точек за 50 миллисекунд и строит 3D-карту лица - три канала инфракрасных данных, те же принципы.

RGB - **аддитивная** модель: цвета складываются. (255, 0, 0) + (0, 255, 0) = (255, 255, 0) - жёлтый. Все три на максимуме (255, 255, 255) - белый. Все на нуле - чёрный. Так работают мониторы: каждый пиксель экрана - три крошечных светодиода.

**OpenCV читает цвет в BGR, не RGB!** Исторически так повелось - формат BMP хранил каналы в порядке Blue-Green-Red. Если загрузить изображение через `cv2.imread()` и отобразить через `matplotlib`, красный и синий поменяются местами.

**HSV (Hue, Saturation, Value)** описывает цвет так, как его воспринимает человек: **оттенок** (H, 0-179 в OpenCV), **насыщенность** (S, 0-255), **яркость** (V, 0-255). Instagram-фильтры живут в HSV: сдвиг Hue превращает летний пейзаж в закатный без единого знания о RGB-значениях листьев.

ПространствоКомпонентыКогда использовать
RGB / BGRRed, Green, BlueОтображение на экране, нейросети
HSVHue, Saturation, ValueСегментация по цвету, трекинг объектов
GrayscaleIntensity (1 канал)Edge detection, feature detection
LABLightness, A, BЦветокоррекция, perceptual difference

Hue в OpenCV - от 0 до 179 (не 360!), потому что 360 не помещается в uint8. Чтобы перевести из стандартных градусов: `H_opencv = H_degrees / 2`.

Вы загрузили фото через cv2.imread() и показали через matplotlib.pyplot.imshow(). Небо на фото выглядит оранжевым вместо голубого. Что произошло?

Каналы: grayscale, RGB, RGBA

**Канал** - одна «слоя» информации в изображении. Grayscale - 1 канал, цветное RGB - 3 канала, изображение с прозрачностью (RGBA) - 4 канала. NumPy-массив хранит это в третьем измерении: **shape = (H, W, C)**, где C - число каналов. Рентгеновский снимок - это один канал с 16-битными значениями плотности ткани. Tesla FSD-камера даёт восемь потоков по три канала.

У grayscale-изображения shape - **(H, W)**, а не (H, W, 1). Это частый источник ошибок при подаче в нейросеть, которая ожидает 3D-тензор. Решение: `gray_3d = gray[:, :, np.newaxis]`.

Каналы можно **разделять** и **объединять**. Это нужно, например, чтобы обработать только один канал (усилить контраст красного) или визуально посмотреть каждый канал отдельно.

**Alpha-канал (A)** определяет прозрачность: 0 - полностью прозрачный, 255 - полностью непрозрачный. PNG поддерживает alpha, JPEG - нет. При наложении изображений (compositing) alpha используется как маска: `result = fg * alpha + bg * (1 - alpha)`.

ТипКаналыShapeРазмер (640x480)
Grayscale1(480, 640)~300 KB
BGR/RGB3(480, 640, 3)~900 KB
BGRA/RGBA4(480, 640, 4)~1.2 MB

Вы загрузили grayscale-изображение: `img = cv2.imread('x.jpg', cv2.IMREAD_GRAYSCALE)`. Какой будет img.shape для фото 1920x1080?

Разрешение и масштабирование

**Разрешение** - количество пикселей по ширине и высоте. 1920x1080 = 2 073 600 пикселей ≈ 2 мегапикселя. Камера iPhone 15 Pro - 48 Мп (8064x6048). Waymo снимает в 360° с разрешением достаточным чтобы читать номерные знаки на 200 метрах. Больше пикселей - больше деталей, но и больше памяти и времени на обработку.

РазрешениеМегапикселиRAM (RGB, uint8)Типичное применение
640x480 (VGA)0.3 Мп~0.9 MBWebcam, real-time CV
1920x1080 (Full HD)2 Мп~6 MBСтандартное видео
3840x2160 (4K)8.3 Мп~24 MBВидеонаблюдение
8064x6048 (48 Мп)48 Мп~139 MBСмартфоны, фотография

**DPI (dots per inch)** и **PPI (pixels per inch)** - часто путают. DPI - для принтеров (сколько точек краски на дюйм). PPI - для экранов (сколько пикселей на дюйм). Фото 300 DPI при печати 10x15 см выглядит отлично; то же фото на экране Retina (218 PPI) может казаться маленьким.

DPI/PPI - свойство **вывода**, а не самого изображения. Файл 4000x3000 остаётся тем же массивом пикселей независимо от того, напечатаете его на визитке или на билборде. Меняется только физический размер каждого пикселя.

При **resize** (масштабировании) пиксели нужно пересчитать. Уменьшение - некоторые пиксели «теряются». Увеличение - нужно **придумать** новые значения. Метод пересчёта называется **интерполяцией**.

МетодСкоростьКачествоКогда использовать
INTER_NEARESTСамый быстрыйПикселизацияPixel art, маски, label maps
INTER_LINEARБыстрыйХорошееDefault, real-time
INTER_AREAСреднийОтличное (↓)Уменьшение изображения
INTER_CUBICМедленныйОтличное (↑)Увеличение, публикация

В `cv2.resize()` размер указывается как **(width, height)**, а не (height, width)! Это единственное место в OpenCV, где порядок - (W, H). Shape массива при этом будет (H, W, C). Будьте внимательны.

Больше мегапикселей = лучше качество фотографии

Качество зависит от совокупности факторов: размер сенсора (больше сенсор → больше света на пиксель → меньше шума), оптика, динамический диапазон, алгоритмы обработки (HDR, computational photography). 12 Мп с большим сенсором (Sony A7) победят 108 Мп с крошечным сенсором в условиях низкой освещённости.

Ключевые идеи

  • Изображение = NumPy-массив, пиксель - одно число (grayscale) или тройка/четвёрка (цветное). Координаты в NumPy: **img[y, x]**, не img[x, y]
  • OpenCV читает цвет в **BGR**, не RGB. Конвертация: `cv2.cvtColor()`. HSV удобнее для сегментации по цвету - оттенок отделён от яркости (Instagram-фильтры живут здесь)
  • Shape = **(H, W, C)**, но в `cv2.resize()` размер - **(W, H)**. Grayscale не имеет оси каналов
  • Tesla убрала радар и выиграла: vision-only оказалось достаточно. Всё начинается с понимания, что изображение - просто таблица чисел

Связанные темы

Пиксели и цветовые пространства - фундамент, на котором строится всё остальное:

  • Фильтрация и свёртка — Свёртка - это операция над соседними пикселями; без понимания координатной системы и каналов невозможно понять, что делает ядро фильтра
  • Признаки: SIFT, SURF, ORB — Feature detection работает на grayscale-канале - сначала нужно конвертировать цветное изображение и понимать, что при этом происходит

Вопросы для размышления

  • Если бы пиксель хранил не 8 бит (0-255), а 16 бит (0-65535) - как бы это изменило медицинскую визуализацию? Подсказка: подумайте о тонких различиях в плотности ткани.
  • Почему HSV лучше RGB для трекинга объекта по цвету при меняющемся освещении? Что происходит с R, G, B-значениями красного мяча, когда облако закрывает солнце?
  • Сколько памяти займёт одна секунда 4K-видео (3840x2160, 30 fps, RGB) без сжатия? Как это соотносится с реальным размером видеофайлов?

Связанные уроки

  • cv-02 — Свёрточные нейронные сети для анализа изображений
  • dl-01 — Deep learning - основа современного computer vision
  • la-01-vectors-intro — Матрицы = пиксельные данные и трансформации
  • dsp-01 — Обработка сигналов: свёртки и частотный анализ
  • aie-26-image-generation — Генерация изображений как CV-приложение
  • prob-01-intro — Вероятностные модели восприятия
Цифровое изображение: пиксели и цвет

0

1

Войти

Маркетинг фокусируется на одном числе - мегапикселях, потому что его просто сравнивать. Но пиксели - лишь «вёдра» для фотонов. Маленький сенсор - маленькие вёдра, мало света, сигнал тонет в шуме. Больше крошечных пикселей только ухудшает ситуацию. Stanford в 2017 показал: AI на обычных рентгенах ставит диагноз точнее дерматолога - не за счёт мегапикселей, а за счёт алгоритма.

Вы уменьшаете фото с 4000x3000 до 200x150 для thumbnails. Какая интерполяция даст лучший результат?