Компьютерное зрение
Цифровое изображение: пиксели и цвет
Предварительные знания
- Базовый 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 / BGR | Red, Green, Blue | Отображение на экране, нейросети |
| HSV | Hue, Saturation, Value | Сегментация по цвету, трекинг объектов |
| Grayscale | Intensity (1 канал) | Edge detection, feature detection |
| LAB | Lightness, 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) |
|---|---|---|---|
| Grayscale | 1 | (480, 640) | ~300 KB |
| BGR/RGB | 3 | (480, 640, 3) | ~900 KB |
| BGRA/RGBA | 4 | (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 MB | Webcam, 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 — Вероятностные модели восприятия