# Домашнє завдання · Зображення очима компʼютера

> Лекція: [lecture.html](lecture.html) · Практика: [practice.ipynb](practice.ipynb) · Тест: [quiz.html](quiz.html)

Усе робимо на тому самому синтетичному фото телефона, що й у практиці: функція
`make_phone_photo()` уже написана в зошиті, скопіюй її до себе й працюй далі.
Нічого завантажувати не треба.

---

## 🟢 Рівень 1 — База

**Паспорт чужого зображення.**

Зміни в генераторі три числа: висоту й ширину знімка (наприклад, на 480 × 640) і колір
корпусу (зроби його світлим, скажімо 200, 205, 210). Згенеруй фото заново й склади
для нього паспорт: `shape`, `dtype`, `min`, `max`, `size`, `nbytes`, а також середнє
по кожному з трьох каналів окремо.

Потім візьми будь-який квадрат 8 × 8 усередині нового корпусу й роздрукуй його
числами в сірому.

**Зроблено, якщо:**

- надруковано всі шість пунктів паспорта й три середні по каналах;
- `nbytes` збігається з тим, що ти порахував руками як `висота × ширина × 3`;
- фрагмент 8 × 8 надруковано, і ти можеш сказати словами, чому в ньому всі числа
  близькі одне до одного, а на межі корпусу — ні.

---

## 🟡 Рівень 2 — Плюс

**Виділення за кольором, яке не боїться освітлення.**

Напиши дві функції, кожна повертає булеву маску того самого розміру, що знімок:

- `select_by_rgb(image)` — три пороги по каналах R, G, B;
- `select_by_hue(image)` — один поріг по відтінку H у HSV.

Обидві мають шукати зелений індикатор. Далі побудуй серію з семи варіантів
освітлення: помнож знімок на 0.4, 0.55, 0.7, 0.85, 1.0, 1.15 і 1.3 (з обрізанням
по 255) і для кожного порахуй, скільки пікселів знайшла кожна функція.

Побудуй графік: по горизонталі — множник яскравості, по вертикалі — кількість
знайдених пікселів, дві криві й горизонтальна пряма на рівні справжньої кількості
(1439 пікселів для оригінального розміру знімка).

**Зроблено, якщо:**

- графік побудовано, обидві криві підписані;
- крива HSV лежить на прямій «скільки треба» майже на всьому діапазоні,
  а крива RGB відходить від неї;
- у висновку двома-трьома реченнями сказано, **чому** так, а не просто «HSV кращий».

---

## 🔴 Рівень 3 — Виклик

**Розтягування гістограми з нуля.**

Реалізуй функцію `stretch_contrast(gray_image)`, яка приймає сіре зображення
й повертає нове, де найтемніший піксель став 0, а найсвітліший — 255, а всі проміжні
розтягнуті пропорційно. Формулу виведи сам: тобі потрібні лише мінімум, максимум
і одна лінійна залежність.

Вимоги до реалізації:

- жодного `cv2.normalize` і жодного циклу по пікселях — тільки арифметика над масивом;
- проміжні обчислення у `float32` або `int16`, обрізання один раз наприкінці;
- функція має коректно поводитись, якщо на вхід прийшло однорідне зображення,
  де мінімум дорівнює максимуму (ділення на нуль ловимо самі).

Застосуй до притемненого варіанта (`gray × 0.45`) і покажи чотири речі поруч:
притемнене фото, його гістограму, розтягнуте фото, його гістограму.

**Зроблено, якщо:**

- після розтягування `min` дорівнює 0, а `max` дорівнює 255 — перевірено `assert`-ом;
- обидві гістограми показано поруч, і видно, що друга займає всю ширину;
- ти можеш пояснити, **що саме розтягування не повертає**: якщо в притемненому знімку
  кілька різних яскравостей злилися в одне число, розтягування їх не розділить.

---

## Підказки

- Порядок каналів. Кожного разу, коли передаєш масив у будь-яку функцію `cv2`, спитай
  себе: вона чекає RGB чи BGR? Помилка тут не викидає винятку — вона просто дає
  неправильну відповідь.

- Рівень 2. Пороги по RGB підбирай на **оригінальному** знімку, а потім більше не
  чіпай. Якщо підганяти їх під кожен рівень освітлення окремо, дослід втрачає сенс:
  уся суть у тому, що в реальній задачі ти не знаєш наперед, яке буде світло.

- Рівень 3. Лінійна залежність, яка переводить відрізок `[min, max]` у відрізок
  `[0, 255]`, будується у два кроки: спершу зсунь так, щоб мінімум став нулем, потім
  розтягни так, щоб максимум став 255. Обидва кроки — одна дія над усім масивом.

- Якщо результат виглядає зашумленим сильніше за оригінал — так і має бути.
  Розтягування підсилює все підряд, шум матриці зокрема.
