OPENCV НА PYTHON | Часть 2 | БАЗОВЫЕ ЗНАНИЯ

OPENCV НА PYTHON | Часть 2 | БАЗОВЫЕ ЗНАНИЯ

Содержание


Урок 8: Обнаружение цветов с помощью HSV цветового пространства

8.1 Теоретическая основа HSV цветового пространства

Суть задачи: Научиться эффективно детектировать объекты по цвету, используя HSV цветовое пространство вместо RGB/BGR.

Теоретическая основа:

HSV (Hue, Saturation, Value) — цветовое пространство, более интуитивное для восприятия человеком:

Hue (Оттенок)

Определяет основной цвет (0-179 в OpenCV, вместо 0-360 в реальности)

Saturation (Насыщенность)

Степень чистоты цвета (0-255): 0 = серый, 255 = полностью насыщенный

Value (Яркость)

Яркость цвета (0-255): 0 = черный, 255 = максимально яркий

Преимущества HSV перед BGR:

Практическое применение: Детектирование цветных маркеров, распознавание объектов по цвету, сегментация изображений.

↑ К оглавлению

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-MaxSat Min-MaxVal 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:

Практический пример:

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

↑ К оглавлению

Ссылки по теме

Конспект:
Вторник, 23 декабря 2025
OPENCV НА PYTHON | Часть 2 | БАЗОВЫЕ ЗНАНИЯ