OPENCV НА PYTHON | Часть 2 | БАЗОВЫЕ ЗНАНИЯ
Содержание
- Урок 8: Обнаружение цветов с помощью HSV цветового пространства
- Урок 9: Обнаружение контуров и классификация геометрических фигур
- Урок 10: Детектирование лиц с помощью каскадов Хаара
- Урок 11: Виртуальная рисовальная доска (Проект 1)
- Урок 12: Сканер документов (Проект 2)
- Чек-лист
Урок 8: Обнаружение цветов с помощью HSV цветового пространства
8.1 Теоретическая основа HSV цветового пространства
Теоретическая основа:
HSV (Hue, Saturation, Value) — цветовое пространство, более интуитивное для восприятия человеком:
Hue (Оттенок)
Определяет основной цвет (0-179 в OpenCV, вместо 0-360 в реальности)
Saturation (Насыщенность)
Степень чистоты цвета (0-255): 0 = серый, 255 = полностью насыщенный
Value (Яркость)
Яркость цвета (0-255): 0 = черный, 255 = максимально яркий
Преимущества HSV перед BGR:
- Независимость от освещения: Hue остается стабильным при изменении яркости
- Интуитивная настройка: легко определить диапазон цветов
- Эффективная сегментация: простое выделение объектов по цвету
8.2 Создание интерактивных трекбаров для настройки параметров
Используемые методы:
| Метод | Описание |
|---|---|
cv2.namedWindow(window_name) |
Создание именованного окна |
cv2.resizeWindow(window_name, width, height) |
Изменение размера окна |
cv2.createTrackbar(name, window, value, count, onChange) |
Создание трекбара |
cv2.getTrackbarPos(name, window) |
Получение значения трекбара |
Практический пример:
import cv2
import numpy as np
# Создание окна для трекбаров
cv2.namedWindow("TrackBars")
cv2.resizeWindow("TrackBars", 640, 240)
# Пустая функция для обратного вызова
def empty(a):
pass
# Создание трекбаров для настройки HSV диапазона
cv2.createTrackbar("Hue Min", "TrackBars", 0, 179, empty)
cv2.createTrackbar("Hue Max", "TrackBars", 19, 179, empty)
cv2.createTrackbar("Sat Min", "TrackBars", 110, 255, empty)
cv2.createTrackbar("Sat Max", "TrackBars", 240, 255, empty)
cv2.createTrackbar("Val Min", "TrackBars", 153, 255, empty)
cv2.createTrackbar("Val Max", "TrackBars", 255, 255, empty)
# Инициализация веб-камеры
cap = cv2.VideoCapture(0)
while True:
# Захват кадра с веб-камеры
success, img = cap.read()
if not success:
break
# Конвертация в HSV
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Получение значений трекбаров
h_min = cv2.getTrackbarPos("Hue Min", "TrackBars")
h_max = cv2.getTrackbarPos("Hue Max", "TrackBars")
s_min = cv2.getTrackbarPos("Sat Min", "TrackBars")
s_max = cv2.getTrackbarPos("Sat Max", "TrackBars")
v_min = cv2.getTrackbarPos("Val Min", "TrackBars")
v_max = cv2.getTrackbarPos("Val Max", "TrackBars")
# Вывод текущих параметров
print(f"Hue: {h_min}-{h_max}, Sat: {s_min}-{s_max}, Val: {v_min}-{v_max}")
# Отображение изображения
cv2.imshow("Original", img)
cv2.imshow("HSV", imgHSV)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
- В OpenCV Hue имеет диапазон 0-179 (вместо 0-360) для экономии памяти
- Трекбары позволяют визуально подобрать оптимальные параметры для конкретного цвета
- Функция обратного вызова обязательна, даже если она пустая
- Значения трекбаров нужно получать в цикле для реального времени
8.3 Создание масок и применение цветовой фильтрации
Используемые методы:
| Метод | Описание |
|---|---|
cv2.inRange(src, lowerb, upperb) |
Создание маски в заданном диапазоне |
cv2.bitwise_and(src1, src2, mask) |
Побитовое И для наложения маски |
Практический пример:
import cv2
import numpy as np
# Чтение изображения
img = cv2.imread('resources/lambo.png')
# Конвертация в HSV
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# Определение диапазона для оранжевого цвета
lower_orange = np.array([5, 50, 50]) # [H_min, S_min, V_min]
upper_orange = np.array([15, 255, 255]) # [H_max, S_max, V_max]
# Создание маски
mask = cv2.inRange(imgHSV, lower_orange, upper_orange)
# Применение маски к исходному изображению
result = cv2.bitwise_and(img, img, mask=mask)
# Отображение результатов
cv2.imshow("Original", img)
cv2.imshow("Mask", mask)
cv2.imshow("Result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Типичные диапазоны HSV для разных цветов:
| Цвет | Hue Min-Max | Sat Min-Max | Val Min-Max |
|---|---|---|---|
| Красный | 0-10 и 160-179 | 100-255 | 100-255 |
| Оранжевый | 10-25 | 100-255 | 100-255 |
| Желтый | 25-35 | 100-255 | 100-255 |
| Зеленый | 40-80 | 100-255 | 100-255 |
| Синий | 90-130 | 100-255 | 100-255 |
| Фиолетовый | 130-160 | 100-255 | 100-255 |
- Маска — бинарное изображение (0 и 255), где 255 соответствует пикселям в диапазоне
- Побитовое И сохраняет только те пиксели исходного изображения, где маска равна 255
- Красный цвет требует два диапазона из-за кругового характера Hue
- Освещение сильно влияет на параметры V (яркость)
8.4 Генерическое решение для детектирования нескольких цветов
Практический пример:
import cv2
import numpy as np
# Список цветов для детектирования: [h_min, s_min, v_min, h_max, s_max, v_max, color_name]
myColors = [
[5, 107, 0, 19, 255, 255, "Orange"],
[133, 56, 0, 159, 156, 255, "Purple"],
[57, 76, 0, 100, 255, 255, "Green"],
[105, 50, 50, 130, 255, 255, "Blue"]
]
# Список цветов для отображения (BGR)
myColorValues = [
[51, 153, 255], # Оранжевый
[255, 0, 255], # Фиолетовый
[0, 255, 0], # Зеленый
[255, 0, 0] # Синий
]
def findColor(img, myColors, myColorValues):
"""
Поиск цветов на изображении и определение их позиций
Args:
img: Исходное изображение
myColors: Список цветовых диапазонов
myColorValues: Список цветов для отображения
Returns:
list: Список найденных точек [[x, y, colorId], ...]
"""
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
count = 0
newPoints = []
for color in myColors:
lower = np.array(color[0:3])
upper = np.array(color[3:6])
mask = cv2.inRange(imgHSV, lower, upper)
# Поиск контуров для определения позиции цвета
contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
area = cv2.contourArea(cnt)
if area > 500: # Фильтрация по площади для удаления шума
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x + w, y + h), myColorValues[count], 2)
newPoints.append([x + w // 2, y, count]) # Центр верхней границы + индекс цвета
count += 1
return newPoints
# Использование функции
cap = cv2.VideoCapture(0)
myPoints = [] # [[x, y, colorId], ...]
while True:
success, img = cap.read()
if not success:
break
newPoints = findColor(img, myColors, myColorValues)
# Сохранение точек для рисования
if newPoints:
for point in newPoints:
myPoints.append(point)
# Рисование всех точек
for point in myPoints:
cv2.circle(img, (point[0], point[1]), 10, myColorValues[point[2]], cv2.FILLED)
cv2.imshow("Result", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
- Решение легко расширяемо: для добавления нового цвета достаточно добавить его в списки
myColorsи myColorValues - Фильтрация по площади
area > 500удаляет мелкий шум и ложные срабатывания - Центр верхней границы объекта
x + w // 2, yиспользуется как точка рисования - Индекс цвета сохраняется для правильного отображения
Урок 9: Обнаружение контуров и классификация геометрических фигур
9.1 Теоретическая основа контурного анализа
Теоретическая основа:
- Контур — непрерывная линия или кривая, соединяющая точки с одинаковой интенсивностью, обычно граница объекта
- Иерархия контуров — отношения между контурами (родитель-потомок) для вложенных объектов
Методы поиска контуров:
| Метод | Описание |
|---|---|
cv2.RETR_EXTERNAL |
Только внешние контуры |
cv2.RETR_LIST |
Все контуры без иерархии |
cv2.RETR_TREE |
Все контуры с полной иерархией |
cv2.RETR_CCOMP |
Все контуры с двумя уровнями иерархии |
Методы аппроксимации контуров:
| Метод | Описание |
|---|---|
cv2.CHAIN_APPROX_NONE |
Все точки контура |
cv2.CHAIN_APPROX_SIMPLE |
Сжатие вертикальных, горизонтальных и диагональных сегментов |
9.2 Поиск контуров и вычисление их характеристик
Используемые методы:
| Метод | Описание |
|---|---|
cv2.findContours(image, mode, method) |
Поиск контуров на бинарном изображении |
cv2.contourArea(contour) |
Вычисление площади контура |
cv2.arcLength(contour, closed) |
Вычисление длины контура (периметра) |
cv2.approxPolyDP(contour, epsilon, closed) |
Аппроксимация контура многоугольником |
cv2.boundingRect(contour) |
Получение ограничивающего прямоугольника |
cv2.drawContours(image, contours, idx, color, thickness) |
Отрисовка контуров |
Практический пример:
import cv2
import numpy as np
def getContours(img, imgContour):
"""
Поиск и анализ контуров для классификации геометрических фигур
Args:
img: Бинарное изображение (результат Canny)
imgContour: Изображение для визуализации результатов
"""
# Поиск контуров
contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
for cnt in contours:
# Вычисление площади контура
area = cv2.contourArea(cnt)
print(f"Area: {area}")
if area > 500: # Фильтрация по площади
# Отрисовка контура
cv2.drawContours(imgContour, cnt, -1, (255, 0, 0), 3)
# Вычисление длины контура (периметра)
peri = cv2.arcLength(cnt, True)
print(f"Perimeter: {peri}")
# Аппроксимация контура
approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
print(f"Corner points: {len(approx)}")
# Получение ограничивающего прямоугольника
x, y, w, h = cv2.boundingRect(approx)
cv2.rectangle(imgContour, (x, y), (x + w, y + h), (0, 255, 0), 2)
# Определение типа фигуры
objType = "Unknown"
if len(approx) == 3:
objType = "Triangle"
elif len(approx) == 4:
# Проверка на квадрат: отношение сторон близко к 1
aspRatio = w / float(h)
if 0.95 < aspRatio < 1.05:
objType = "Square"
else:
objType = "Rectangle"
elif len(approx) > 4:
objType = "Circle"
# Отображение типа фигуры
cv2.putText(imgContour, objType,
(x + (w // 2) - 30, y + (h // 2)),
cv2.FONT_HERSHEY_COMPLEX, 0.7, (0, 0, 0), 2)
# Основной код
img = cv2.imread('resources/shapes.png')
imgContour = img.copy()
# Предварительная обработка
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgBlur = cv2.GaussianBlur(imgGray, (7, 7), 1)
imgCanny = cv2.Canny(imgBlur, 50, 50)
# Поиск контуров
getContours(imgCanny, imgContour)
# Отображение результатов
cv2.imshow("Original", img)
cv2.imshow("Canny", imgCanny)
cv2.imshow("Contours", imgContour)
cv2.waitKey(0)
cv2.destroyAllWindows()
- Контурный анализ работает с бинарными изображениями (результат
Cannyилиthreshold) - Фильтрация по площади
area > 500удаляет мелкие контуры-шум epsilon = 0.02 * peri— стандартное значение для аппроксимации (2% от периметра)- Для распознавания квадратов проверяется отношение сторон
aspRatio cv2.drawContours с contourIdx = -1рисует все контуры в списке
9.3 Расширенный анализ контуров и их свойств
1. Центр массы контура:
M = cv2.moments(cnt)
if M["m00"] != 0: # Проверка деления на ноль
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
2. Выпуклая оболочка:
hull = cv2.convexHull(cnt)
cv2.drawContours(img, [hull], -1, (0, 255, 0), 2)
3. Дефекты выпуклости (для распознавания пальцев):
hull = cv2.convexHull(cnt, returnPoints=False)
defects = cv2.convexityDefects(cnt, hull)
4. Минимальная площадь и минимальный круг:
# Минимальный ограничивающий прямоугольник (с поворотом)
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
# Минимальный ограничивающий круг
(x, y), radius = cv2.minEnclosingCircle(cnt)
5. Эллипс минимальной площади:
ellipse = cv2.fitEllipse(cnt)
cv2.ellipse(img, ellipse, (0, 255, 0), 2)
- Моменты
cv2.momentsпредоставляют информацию о форме и распределении массы - Выпуклая оболочка помогает определить "вогнутости" объекта
- Минимальная ограничивающая фигура полезна для нормализации ориентации
- Дефекты выпуклости применяются в распознавании жестов рук
Урок 10: Детектирование лиц с помощью каскадов Хаара
10.1 Теоретическая основа метода Виолы-Джонса
Теоретическая основа:
Исторический контекст: Алгоритм Виолы-Джонса (2001) был первым, обеспечивающим детектирование лиц в реальном времени.
Основные компоненты:
Интегральное изображение
Быстрое вычисление суммы пикселей в прямоугольнике
Каскад классификаторов
Последовательность простых классификаторов для быстрого отсеивания
Признаки Хаара
Простые прямоугольные признаки для описания локальных особенностей
Алгоритм AdaBoost
Выбор лучших признаков и создание сильного классификатора
Преимущества и ограничения:
| Преимущества | Ограничения |
|---|---|
| Высокая скорость работы | Плохо работает с повернутыми лицами |
| Хорошая точность для фронтальных лиц | Требует предварительно обученных каскадов |
| Низкие требования к вычислительным ресурсам | Может давать ложные срабатывания |
10.2 Практическая реализация детектирования лиц
Используемые методы:
| Метод | Описание |
|---|---|
cv2.CascadeClassifier(filename) |
Загрузка предобученного каскада |
detectMultiScale(image, scaleFactor, minNeighbors, minSize, maxSize) |
Детектирование объектов |
Параметры detectMultiScale:
image— изображение в градациях серогоscaleFactor— параметр масштабирования (1.05-1.3)minNeighbors— минимальное количество соседей для подтверждения обнаружения (3-6)minSize, maxSize— минимальный и максимальный размер объекта
Практический пример:
import cv2
# Загрузка предобученного каскада для детектирования лиц
faceCascade = cv2.CascadeClassifier("resources/haarcascade_frontalface_default.xml")
# Загрузка изображения
img = cv2.imread('resources/lena.png')
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Детектирование лиц
faces = faceCascade.detectMultiScale(
imgGray,
scaleFactor=1.1, # Уменьшение масштаба на 10% на каждом шаге
minNeighbors=4, # Минимум 4 совпадения для подтверждения
minSize=(30, 30) # Минимальный размер лица
)
# Отрисовка прямоугольников вокруг обнаруженных лиц
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
cv2.putText(img, "Face", (x, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 0), 2)
# Детектирование глаз на том же изображении
eyeCascade = cv2.CascadeClassifier("resources/haarcascade_eye.xml")
for (x, y, w, h) in faces:
roiGray = imgGray[y:y + h, x:x + w]
roiColor = img[y:y + h, x:x + w]
eyes = eyeCascade.detectMultiScale(roiGray, scaleFactor=1.1, minNeighbors=5)
for (ex, ey, ew, eh) in eyes:
cv2.rectangle(roiColor, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2)
# Отображение результата
cv2.imshow("Face Detection", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Доступные предобученные каскады:
| Файл | Назначение |
|---|---|
haarcascade_frontalface_default.xml |
Фронтальные лица |
haarcascade_eye.xml |
Глаза |
haarcascade_smile.xml |
Улыбки |
haarcascade_fullbody.xml |
Полный рост человека |
- Всегда конвертируйте изображение в градации серого перед детектированием
scaleFactor=1.1означает уменьшение масштаба на 10% на каждом шаге (меньше = точнее, но медленнее)minNeighbors=4требует минимум 4 совпадающих прямоугольника- Для детектирования глаз используйте
ROI(Region of Interest) — область лица
10.3 Детектирование лиц в реальном времени
Практический пример:
import cv2
# Загрузка каскадов
faceCascade = cv2.CascadeClassifier("resources/haarcascade_frontalface_default.xml")
eyeCascade = cv2.CascadeClassifier("resources/haarcascade_eye.xml")
# Инициализация веб-камеры
cap = cv2.VideoCapture(0)
cap.set(3, 640) # Ширина
cap.set(4, 480) # Высота
cap.set(10, 150) # Яркость
while True:
success, img = cap.read()
if not success:
break
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Детектирование лиц
faces = faceCascade.detectMultiScale(imgGray, 1.1, 4)
# Обработка каждого обнаруженного лица
for (x, y, w, h) in faces:
# Отрисовка прямоугольника вокруг лица
cv2.rectangle(img, (x, y), (x + w, y + h), (255, 0, 0), 2)
cv2.putText(img, f"Face {w}x{h}", (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
# ROI для детектирования глаз
roiGray = imgGray[y:y + h, x:x + w]
roiColor = img[y:y + h, x:x + w]
# Детектирование глаз
eyes = eyeCascade.detectMultiScale(roiGray, 1.1, 5)
for (ex, ey, ew, eh) in eyes:
cv2.rectangle(roiColor, (ex, ey), (ex + ew, ey + eh), (0, 255, 0), 2)
# Отображение количества обнаруженных лиц
cv2.putText(img, f"Faces detected: {len(faces)}", (20, 450),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
cv2.imshow("Face Detection", img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
- В реальном времени важно балансировать между скоростью и точностью
scaleFactor=1.1иminNeighbors=4— хороший баланс для веб-камеры- Отображение размера лица
w x hпомогает в настройке параметров - Для мобильных устройств можно уменьшить разрешение кадра для повышения производительности
Урок 11: Виртуальная рисовальная доска (Проект 1)
11.1 Постановка задачи и архитектура проекта
Требования к системе:
- Работа в реальном времени с веб-камерой
- Детектирование нескольких цветов одновременно
- Плавное рисование с минимальной задержкой
- Возможность стирания и очистки холста
- Интуитивный интерфейс пользователя
Архитектура проекта:
Входной модуль
Захват видео с веб-камеры
Модуль обработки
Детектирование цветов и позиций маркеров
Модуль рисования
Управление точками и отрисовка на холсте
Модуль интерфейса
Отображение результатов и управление
11.2 Реализация основного функционала
Ключевые компоненты:
import cv2
import numpy as np
# Конфигурация цветов: [Hmin, Smin, Vmin, Hmax, Smax, Vmax, Name]
myColors = [
[5, 107, 0, 19, 255, 255, "Orange"],
[133, 56, 0, 159, 156, 255, "Purple"],
[57, 76, 0, 100, 255, 255, "Green"],
[105, 50, 50, 130, 255, 255, "Blue"]
]
# Цвета для рисования (BGR)
myColorValues = [
[51, 153, 255], # Оранжевый
[255, 0, 255], # Фиолетовый
[0, 255, 0], # Зеленый
[255, 0, 0] # Синий
]
# Холст для рисования
myPoints = [] # [[x, y, colorId], ...]
def findColor(img, myColors, myColorValues):
"""
Поиск цветов и определение позиций маркеров
"""
imgHSV = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
newPoints = []
for i, color in enumerate(myColors):
lower = np.array(color[0:3])
upper = np.array(color[3:6])
mask = cv2.inRange(imgHSV, lower, upper)
# Улучшение маски с помощью морфологических операций
kernel = np.ones((5, 5), np.uint8)
mask = cv2.erode(mask, kernel, iterations=1)
mask = cv2.dilate(mask, kernel, iterations=2)
# Поиск контуров
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Нахождение самого большого контура
max_area = 0
max_cnt = None
for cnt in contours:
area = cv2.contourArea(cnt)
if area > 1000 and area > max_area:
max_area = area
max_cnt = cnt
if max_cnt is not None:
x, y, w, h = cv2.boundingRect(max_cnt)
cx, cy = x + w // 2, y # Центр верхней границы
# Визуальная обратная связь
cv2.circle(img, (cx, cy), 15, myColorValues[i], cv2.FILLED)
newPoints.append([cx, cy, i])
return newPoints
def drawOnCanvas(myPoints, myColorValues, img):
"""
Отрисовка точек на изображении
"""
for point in myPoints:
cv2.circle(img, (point[0], point[1]), 10, myColorValues[point[2]], cv2.FILLED)
# Основной цикл
cap = cv2.VideoCapture(0)
cap.set(3, 1280) # Увеличенное разрешение
cap.set(4, 720)
cap.set(10, 150)
while True:
success, img = cap.read()
if not success:
break
img = cv2.flip(img, 1) # Зеркальное отображение
imgResult = img.copy()
# Поиск цветов
newPoints = findColor(imgResult, myColors, myColorValues)
# Добавление новых точек
if newPoints:
for point in newPoints:
myPoints.append(point)
# Отрисовка всех точек
if myPoints:
drawOnCanvas(myPoints, myColorValues, imgResult)
# Отображение интерфейса
cv2.putText(imgResult, "Virtual Paint", (10, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3)
cv2.putText(imgResult, "Press 'c' to clear, 'q' to quit", (10, 100),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (200, 200, 200), 2)
cv2.imshow("Virtual Paint", imgResult)
# Управление
key = cv2.waitKey(1)
if key == ord('c'):
myPoints = []
elif key == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
- Зеркальное отображение
cv2.flip(img, 1)делает интерфейс интуитивным - Морфологические операции улучшают качество маски и удаляют шум
- Фильтрация по площади
area > 1000отсекает мелкие объекты - Визуальная обратная связь (круги на кончиках маркеров) улучшает UX
11.3 Оптимизация и улучшение проекта
Оптимизации:
- Сглаживание траектории: Использование скользящего среднего для плавного рисования
- Адаптивная чувствительность: Изменение параметров в зависимости от освещения
- Фильтрация дрожания: Удаление аномальных значений позиций
- Управление толщиной линии: Изменение диаметра кисти
Улучшенная версия с плавным рисованием:
# Глобальные переменные для новых функций
brushThickness = 15
xp, yp = 0, 0 # Предыдущие координаты
imgCanvas = np.zeros((720, 1280, 3), np.uint8) # Чистый холст
# Внутри основного цикла:
if newPoints:
for point in newPoints:
x, y = point[0:2]
if xp == 0 and yp == 0: # Первый кадр
xp, yp = x, y
# Плавное рисование с использованием линии между точками
cv2.line(imgCanvas, (xp, yp), (x, y), myColorValues[point[2]], brushThickness)
xp, yp = x, y # Обновление предыдущих координат
else:
xp, yp = 0, 0 # Сброс при отсутствии маркера
# Наложение холста на изображение камеры
imgGray = cv2.cvtColor(imgCanvas, cv2.COLOR_BGR2GRAY)
_, imgInv = cv2.threshold(imgGray, 50, 255, cv2.THRESH_BINARY_INV)
imgInv = cv2.cvtColor(imgInv, cv2.COLOR_GRAY2BGR)
imgResult = cv2.bitwise_and(img, imgInv)
imgResult = cv2.bitwise_or(imgResult, imgCanvas)
- Разделение холста и изображения позволяет сохранять качество рисунка
- Плавное рисование с линиями вместо отдельных точек создает непрерывные линии
- Наложение холста с помощью битовых операций создает эффект рисования на изображении
Урок 12: Сканер документов (Проект 2)
12.1 Постановка задачи и теоретическая основа
Требования к системе:
- Автоматическое детектирование документа на изображении
- Коррекция перспективы для получения прямоугольного вида
- Улучшение качества изображения для лучшей читаемости
- Работа с документами разного формата и ориентации
Теоретическая основа:
Перспективное преобразование
Математическое преобразование, корректирующее искажение перспективы
Гомография
Проективное преобразование между двумя плоскостями
Алгоритм обнаружения углов
Поиск характерных точек документа
Адаптивная пороговая обработка
Улучшение контрастности для лучшего детектирования
12.2 Предварительная обработка изображения
Последовательность операций:
| Операция | Назначение |
|---|---|
| Конвертация в градации серого | Уменьшение размерности данных |
| Размытие по Гауссу | Уменьшение шума и сглаживание |
| Детектирование границ (Canny) | Выделение контуров документа |
| Дилатация | Утолщение границ для соединения разорванных линий |
| Эрозия | Утончение границ для удаления мелких деталей |
Практический пример:
import cv2
import numpy as np
# Параметры изображения
widthImg = 640
heightImg = 480
def preProcessing(img):
"""
Предварительная обработка изображения для детектирования документа
Args:
img (numpy.ndarray): Исходное изображение
Returns:
numpy.ndarray: Обработанное бинарное изображение
"""
# Изменение размера для стандартизации обработки
img = cv2.resize(img, (widthImg, heightImg))
# Конвертация в градации серого
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Гауссово размытие
imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)
# Детектирование границ Canny
imgCanny = cv2.Canny(imgBlur, 200, 200)
# Морфологические операции для улучшения контуров
kernel = np.ones((5, 5), np.uint8)
imgDilate = cv2.dilate(imgCanny, kernel, iterations=2)
imgEroded = cv2.erode(imgDilate, kernel, iterations=1)
return imgEroded
- Последовательность операций критична: сначала размытие, потом детектирование границ
- Размер ядра (5, 5) подходит для большинства документных изображений
- Количество итераций дилатации и эрозии влияет на качество соединения границ
- Пороги Canny (200, 200) обеспечивают детектирование только значимых границ
12.3 Детектирование контуров документа
Практический пример:
def getContours(img, imgOriginal):
"""
Поиск контура документа и определение угловых точек
Args:
img: Бинарное изображение после предобработки
imgOriginal: Исходное изображение для визуализации
Returns:
numpy.ndarray: Угловые точки документа
numpy.ndarray: Изображение с контурами
"""
contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
biggest = np.array([])
maxArea = 0
imgWithContours = imgOriginal.copy()
for cnt in contours:
area = cv2.contourArea(cnt)
if area > 5000: # Минимальная площадь
peri = cv2.arcLength(cnt, True)
approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
# Проверка на четырехугольник
if area > maxArea and len(approx) == 4:
biggest = approx
maxArea = area
cv2.drawContours(imgWithContours, cnt, -1, (0, 255, 0), 2)
# Сортировка угловых точек
if biggest.size != 0:
biggest = biggest.reshape((4, 2))
add = biggest.sum(1)
diff = np.diff(biggest, axis=1)
pts = np.zeros((4, 1, 2), dtype=np.int32)
pts[0] = biggest[np.argmin(add)] # Верхний левый
pts[3] = biggest[np.argmax(add)] # Нижний правый
pts[1] = biggest[np.argmin(diff)] # Верхний правый
pts[2] = biggest[np.argmax(diff)] # Нижний левый
cv2.drawContours(imgWithContours, pts, -1, (0, 0, 255), 20)
return pts, imgWithContours
return biggest, imgWithContours
- Фильтрация по площади
area > 5000отсекает мелкие объекты - Аппроксимация контура с точностью
2%от периметра определяет углы - Проверка на четырехугольник
len(approx) == 4гарантирует детектирование документа - Правильная сортировка углов критична для корректного преобразования
12.4 Перспективное преобразование и постобработка
Используемые методы:
| Метод | Описание |
|---|---|
cv2.getPerspectiveTransform() |
Вычисление матрицы перспективного преобразования |
cv2.warpPerspective() |
Применение перспективного преобразования |
cv2.adaptiveThreshold() |
Бинаризация для лучшей читаемости |
Полный код функций:
def getWarp(img, biggest):
"""
Применение перспективного преобразования к изображению документа
"""
if biggest.size == 0:
return img
# Определение целевых точек
pts1 = np.float32(biggest)
pts2 = np.float32([[0, 0], [widthImg, 0], [0, heightImg], [widthImg, heightImg]])
# Вычисление матрицы преобразования
matrix = cv2.getPerspectiveTransform(pts1, pts2)
# Применение преобразования
imgOutput = cv2.warpPerspective(img, matrix, (widthImg, heightImg))
# Обрезка краев для удаления артефактов
imgOutput = imgOutput[20:imgOutput.shape[0] - 20, 20:imgOutput.shape[1] - 20]
imgOutput = cv2.resize(imgOutput, (widthImg, heightImg))
return imgOutput
def enhanceDocument(img):
"""
Улучшение качества документа для лучшей читаемости
"""
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Адаптивная пороговая обработка
imgEnhanced = cv2.adaptiveThreshold(imgGray, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
imgEnhanced = cv2.cvtColor(imgEnhanced, cv2.COLOR_GRAY2BGR)
return imgEnhanced
# Основной цикл программы
cap = cv2.VideoCapture(0)
cap.set(3, widthImg)
cap.set(4, heightImg)
while True:
success, img = cap.read()
if not success:
break
img = cv2.resize(img, (widthImg, heightImg))
imgOriginal = img.copy()
imgProcessed = preProcessing(img)
# Детектирование контуров документа
biggest, imgWithContours = getContours(imgProcessed, imgOriginal)
if biggest.size != 0:
# Перспективное преобразование
imgWarped = getWarp(imgOriginal, biggest)
# Улучшение качества документа
imgEnhanced = enhanceDocument(imgWarped)
cv2.imshow("Document", imgEnhanced)
cv2.imshow("Original", imgOriginal)
cv2.imshow("Processed", imgProcessed)
# Управление: 's' для сохранения, 'q' для выхода
key = cv2.waitKey(1)
if key == ord('s') and biggest.size != 0:
cv2.imwrite("scanned_document.jpg", imgEnhanced)
print("Document saved!")
elif key == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
- Автоматическое детектирование документа без ручного указания углов
- Постобработка с адаптивной пороговой обработкой улучшает читаемость текста
- Обрезка краев после преобразования удаляет артефакты
- Сохранение результата позволяет сохранять отсканированные документы
- Размещайте документ на контрастном фоне
- Обеспечьте равномерное освещение для минимизации теней
- Держите камеру перпендикулярно документу для минимизации искажений
- Для сложных документов можно добавить ручную корректировку угловых точек
ЧЕК-ЛИСТ ПО 2 ЧАСТИ: УРОКИ 8-12
HSV цветовое пространство и детектирование цветов
| ✓ | Задача |
|---|---|
| Понимаю различия между BGR и HSV цветовыми пространствами | |
| Знаю диапазоны значений для H (0-179), S (0-255) и V (0-255) в OpenCV | |
| Умею создавать трекбары для интерактивной настройки параметров | |
Могу конвертировать изображение в HSV: cv2.cvtColor(img, cv2.COLOR_BGR2HSV) |
|
Умею создавать маски с помощью cv2.inRange() |
|
Знаю, как применять маски с cv2.bitwise_and() |
|
| Понимаю типичные диапазоны HSV для основных цветов | |
| Реализовал систему детектирования нескольких цветов одновременно |
Контурный анализ и классификация фигур
| ✓ | Задача |
|---|---|
| Понимаю, что такое контур и иерархия контуров | |
| Знаю различия между режимами поиска контуров (RETR_EXTERNAL, RETR_TREE и т.д.) | |
Умею находить контуры с помощью cv2.findContours() |
|
Могу вычислять площадь контура: cv2.contourArea() |
|
Умею вычислять периметр контура: cv2.arcLength() |
|
Знаю, как аппроксимировать контур многоугольником: cv2.approxPolyDP() |
|
Могу получать ограничивающий прямоугольник: cv2.boundingRect() |
|
| Реализовал классификацию геометрических фигур по количеству углов | |
| Знаю дополнительные методы анализа контуров (моменты, выпуклая оболочка) |
Детектирование лиц с каскадами Хаара
| ✓ | Задача |
|---|---|
| Понимаю принцип работы алгоритма Виолы-Джонса | |
| Знаю основные компоненты: интегральное изображение, признаки Хаара, AdaBoost | |
Умею загружать предобученные каскады: cv2.CascadeClassifier() |
|
Могу детектировать лица с помощью detectMultiScale() |
|
Понимаю параметры scaleFactor и minNeighbors |
|
| Умею использовать ROI для детектирования глаз внутри области лица | |
| Реализовал детектирование лиц в реальном времени с веб-камеры | |
| Знаю доступные предобученные каскады (лица, глаза, улыбки и т.д.) |
Виртуальная рисовальная доска (Проект 1)
| ✓ | Задача |
|---|---|
| Понимаю архитектуру системы виртуальной рисовальной доски | |
| Реализовал детектирование цветных маркеров в реальном времени | |
| Умею применять морфологические операции для улучшения маски | |
| Реализовал фильтрацию по площади для отсеивания шума | |
| Создал систему рисования с сохранением точек | |
| Реализовал плавное рисование линиями вместо отдельных точек | |
| Добавил функции очистки холста и управления с клавиатуры | |
| Создал интуитивный интерфейс с визуальной обратной связью |
Сканер документов (Проект 2)
| ✓ | Задача |
|---|---|
| Понимаю теоретическую основу перспективного преобразования | |
| Реализовал предварительную обработку изображения для детектирования документа | |
| Умею находить контур документа и определять угловые точки | |
| Знаю, как сортировать угловые точки в правильном порядке | |
Могу применять перспективное преобразование: cv2.getPerspectiveTransform() и cv2.warpPerspective() |
|
| Реализовал обрезку краев для удаления артефактов | |
| Умею применять адаптивную пороговую обработку для улучшения читаемости | |
| Создал полнофункциональную систему сканирования документов |
Общие замечания для самопроверки
| ✓ | Задача |
|---|---|
| Все проекты работают стабильно без сбоев | |
| Производительность оптимизирована для работы в реальном времени | |
| Код хорошо документирован с комментариями | |
| Все внешние зависимости (OpenCV, NumPy) установлены | |
| Тестирование проведено на различных изображениях и условиях | |
| Визуальная обратная связь понятна пользователю | |
| Все ресурсы (изображения, каскады) доступны в папке resources |

