Изображение как данные: пиксели, каналы и градации серого

Главная мысль раздела: для машины картинка — не «изображение», а двумерная таблица чисел.

Пиксель (pixel) — наименьший элемент растрового изображения; для него хранится яркость (в сером) или набор значений каналов (в цвете).

Картинка — это матрица

Чёрно-белое (точнее, в градациях серого) изображение — это сетка пикселей, где каждое число задаёт яркость. Обычно яркость кодируют одним байтом: 0 — чёрный, 255 — белый, между ними — оттенки серого. Соберём из чисел маленькую «картинку» 5×5 и нарисуем её символами: яркие пиксели как #, тёмные как ..

img = [
    [0,   0,   0,   0,   0],
    [0, 255, 255, 255,   0],
    [0, 255,   0, 255,   0],
    [0, 255, 255, 255,   0],
    [0,   0,   0,   0,   0],
]

for row in img:
    line = "".join("#" if p > 128 else "." for p in row)
    print(line)

print()
print("Размер:", len(img), "x", len(img[0]))
print("Пиксель [2][2]:", img[2][2])

Вывод:

.....
.###.
.#.#.
.###.
.....

Размер: 5 x 5
Пиксель [2][2]: 0

Видно квадратную «рамку» из ярких пикселей с тёмным центром. Это и есть изображение для компьютера: индексы [y][x] — координаты, значение — яркость. Никакой магии — обычная вложенная таблица.

Цвет: три канала RGB

Цветное изображение хранит на каждый пиксель не одно число, а три — интенсивности красного (R), зелёного (G) и синего (B). Картинка превращается в три наложенных матрицы — каналы. Смешивая R, G, B, получают любой цвет: (255,0,0) — красный, (0,255,0) — зелёный, (255,255,255) — белый, (0,0,0) — чёрный.

RGBЦвет
(255, 0, 0)красный
(0, 255, 0)зелёный
(0, 0, 255)синий
(255, 255, 0)жёлтый (R+G)
(0, 0, 0)чёрный

Так фото 1000×1000 в цвете — это 1000×1000×3 = 3 миллиона чисел. Для нейросети это вход тензора формы (высота, ширина, каналы).

Из цвета в серый

Часто цвет не нужен — форма объекта важнее окраски. Тогда три канала сворачивают в один по формуле яркости, которая учитывает, что глаз чувствительнее к зелёному и слабее к синему:

pixels = [
    (255,   0,   0),
    (  0, 255,   0),
    (  0,   0, 255),
    (255, 255, 255),
    (  0,   0,   0),
    (128, 128, 128),
]

for (r, g, b) in pixels:
    gray = round(0.299 * r + 0.587 * g + 0.114 * b)
    print(f"RGB({r:3},{g:3},{b:3}) -> {gray}")

Вывод:

RGB(255,  0,  0) -> 76
RGB(  0,255,  0) -> 150
RGB(  0,  0,255) -> 29
RGB(255,255,255) -> 255
RGB(  0,  0,  0) -> 0
RGB(128,128,128) -> 128

Обратите внимание: чисто зелёный даёт яркость 150, а чисто синий — всего 29. Коэффициенты 0.299 / 0.587 / 0.114 подобраны под восприятие человеческого глаза (стандарт luma). Простое среднее (r+g+b)/3 тоже работает, но выглядит «неправильно»: синее небо кажется слишком светлым.

Итог

  • Изображение для машины — матрица чисел, индексы [y][x] — координаты пикселя.
  • Серое изображение — одна матрица яркостей 0..255.
  • Цветное — три канала R, G, B; форма входа нейросети (H, W, 3).
  • Перевод в серый — взвешенная сумма каналов под чувствительность глаза.
Проверьте себя
1. Как хранится один пиксель цветного RGB-изображения?
AОдним числом яркости
BТремя числами — интенсивностями красного, зелёного и синего
CСтрокой с названием цвета
DЧетырьмя координатами
2. Почему при переводе в серый зелёному дают коэффициент 0.587, а синему 0.114?
AЭто случайные числа
BЗелёных пикселей на матрице больше
CГлаз человека чувствительнее к зелёному и слабее к синему — коэффициенты под восприятие
DТак быстрее считать
3. Сколько чисел хранит цветное изображение 100×100 пикселей в RGB?
A100
B10 000
C30 000
D300
Поддержать проект