Суть задачи: Научиться эффективно детектировать объекты по цвету, используя 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:
- Независимость от освещения: Hue остается стабильным при изменении яркости
- Интуитивная настройка: легко определить диапазон цветов
- Эффективная сегментация: простое выделение объектов по цвету
Практическое применение: Детектирование цветных маркеров, распознавание объектов по цвету, сегментация изображений.
К чек-листу
Суть задачи: Разработать интерфейс для интерактивной настройки параметров цветового детектирования в реальном времени.
Используемые методы:
cv2.namedWindow(window_name) - создание именованного окна
cv2.resizeWindow(window_name, width, height) - изменение размера окна
cv2.createTrackbar(name, window, value, count, onChange) - создание трекбара:
name - название трекбара
window - окно, где разместить трекбар
value - начальное значение
count - максимальное значение
onChange - функция обратного вызова (может быть пустой)
Практический пример:
Важные особенности:
- В OpenCV Hue имеет диапазон 0-179 (вместо 0-360) для экономии памяти
- Трекбары позволяют визуально подобрать оптимальные параметры для конкретного цвета
- Функция обратного вызова обязательна, даже если она пустая
- Значения трекбаров нужно получать в цикле для реального времени
К чек-листу
Суть задачи: Научиться создавать бинарные маски для выделения областей заданного цвета.
Используемые методы:
cv2.inRange(src, lowerb, upperb) - создание маски в заданном диапазоне:
src - исходное изображение (обычно HSV)
lowerb - нижняя граница диапазона (numpy массив)
upperb - верхняя граница диапазона (numpy массив)
cv2.bitwise_and(src1, src2, mask) - побитовое И для наложения маски:
src1, src2 - исходные изображения
mask - бинарная маска для фильтрации
Практический пример:
Важные особенности:
- Маска - бинарное изображение (0 и 255), где 255 соответствует пикселям в диапазоне
- Побитовое И сохраняет только те пиксели исходного изображения, где маска равна 255
- Для разных цветов нужны разные диапазоны HSV:
- Красный: ~[0-10, 100-255, 100-255] и [160-179, 100-255, 100-255] (из-за кругового Hue)
- Зеленый: ~[40-80, 100-255, 100-255]
- Синий: ~[90-130, 100-255, 100-255]
- Освещение сильно влияет на параметры V (яркость)
К чек-листу
Суть задачи: Создать универсальное решение для одновременного детектирования нескольких цветов.
Практический пример:
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):
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) используется как точка рисования
- Индекс цвета сохраняется для правильного отображения
К чек-листу
Суть задачи: Научиться находить и анализировать контуры объектов для их идентификации и классификации.
Теоретическая основа:
- Контур - непрерывная линия или кривая, соединяющая точки с одинаковой интенсивностью, обычно граница объекта
- Иерархия контуров - отношения между контурами (родитель-потомок) для вложенных объектов
Методы поиска контуров:
cv2.RETR_EXTERNAL - только внешние контуры
cv2.RETR_LIST - все контуры без иерархии
cv2.RETR_TREE - все контуры с полной иерархией
cv2.RETR_CCOMP - все контуры с двумя уровнями иерархии
Методы аппроксимации контуров:
cv2.CHAIN_APPROX_NONE - все точки контура
cv2.CHAIN_APPROX_SIMPLE - сжатие вертикальных, горизонтальных и диагональных сегментов
Практическое применение: Распознавание геометрических фигур, подсчет объектов, измерение площадей, распознавание символов.
К чек-листу
Суть задачи: Реализовать алгоритм для нахождения контуров и вычисления их основных характеристик.
Используемые методы:
cv2.findContours(image, mode, method) - поиск контуров
cv2.contourArea(contour) - вычисление площади контура
cv2.arcLength(contour, closed) - вычисление длины контура
cv2.approxPolyDP(contour, epsilon, closed) - аппроксимация контура многоугольником
cv2.boundingRect(contour) - получение ограничивающего прямоугольника
cv2.drawContours(image, contours, contourIdx, color, thickness) - отрисовка контуров
Практический пример:
import cv2
import numpy as np
def getContours(img, 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)
# Отображение результатов
imgStack = stackImages(0.6, ([img, imgGray, imgBlur], [imgCanny, imgContour, np.zeros_like(img)]))
cv2.imshow("Shape Detection", imgStack)
cv2.waitKey(0)
cv2.destroyAllWindows()
Важные особенности:
- Контурный анализ работает с бинарными изображениями (результат Canny или threshold)
- Фильтрация по площади (
area > 500) удаляет мелкие контуры-шум
epsilon = 0.02 * peri - стандартное значение для аппроксимации (2% от периметра)
- Для распознавания квадратов проверяется отношение сторон (
aspRatio)
cv2.drawContours с contourIdx = -1 рисует все контуры в списке
К чек-листу
Суть задачи: Изучить дополнительные свойства контуров для более точной классификации объектов.
Центр массы контура:
Выпуклая оболочка:
Дефекты выпуклости (для распознавания пальцев):
Минимальная площадь и минимальный круг:
Эллипс минимальной площади:
Важные особенности:
- Моменты (
cv2.moments) предоставляют информацию о форме и распределении массы
- Выпуклая оболочка помогает определить "вогнутости" объекта
- Минимальная ограничивающая фигура полезна для нормализации ориентации
- Дефекты выпуклости применяются в распознавании жестов рук
К чек-листу
Суть задачи: Понять принцип работы алгоритма Виолы-Джонса для детектирования объектов в реальном времени.
Теоретическая основа:
Исторический контекст: Алгоритм Виолы-Джонса (2001) был первым, обеспечивающим детектирование лиц в реальном времени
Основные компоненты:
- Интегральное изображение - быстрое вычисление суммы пикселей в прямоугольнике
- Каскад классификаторов - последовательность простых классификаторов
- Признаки Хаара - простые прямоугольные признаки для описания локальных особенностей
- Алгоритм AdaBoost - выбор лучших признаков и создание сильного классификатора
Преимущества метода:
- Высокая скорость работы
- Хорошая точность для фронтальных лиц
- Низкие требования к вычислительным ресурсам
Ограничения:
- Плохо работает с повернутыми лицами
- Требует предварительно обученных каскадов
- Может давать ложные срабатывания
Практическое применение: Системы безопасности, фотоаппараты, социальные сети, системы контроля доступа.
К чек-листу
Суть задачи: Реализовать детектирование лиц с использованием предобученных каскадов Хаара.
Используемые методы:
cv2.CascadeClassifier(filename) - загрузка предобученного каскада
detectMultiScale(image, scaleFactor, minNeighbors, 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, # Минимальное количество соседних прямоугольников для подтверждения
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()
Важные особенности:
- Всегда конвертируйте изображение в градации серого перед детектированием
scaleFactor=1.1 означает уменьшение масштаба на 10% на каждом шаге
minNeighbors=4 требует минимум 4 совпадающих прямоугольника для подтверждения
- Для детектирования глаз используйте ROI (Region of Interest) - область лица
- Предобученные каскады: haarcascade_frontalface_default.xml, haarcascade_eye.xml, haarcascade_smile.xml
К чек-листу
Суть задачи: Реализовать систему детектирования лиц с веб-камеры в реальном времени.
Практический пример:
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 - хороший баланс для веб-камеры
- Отображение размера лица помогает в настройке параметров
- Для мобильных устройств можно уменьшить разрешение кадра для повышения производительности
К чек-листу
Суть проекта: Создать систему виртуальной рисовальной доски, где пользователь может рисовать в воздухе с помощью цветных маркеров.
Требования к системе:
- Работа в реальном времени с веб-камерой
- Детектирование нескольких цветов одновременно
- Плавное рисование с минимальной задержкой
- Возможность стирания и очистки холста
- Интуитивный интерфейс пользователя
Архитектура проекта:
- Входной модуль: Захват видео с веб-камеры
- Модуль обработки: Детектирование цветов и позиций маркеров
- Модуль рисования: Управление точками и отрисовка на холсте
- Модуль интерфейса: Отображение результатов и управление
К чек-листу
Суть задачи: Реализовать детектирование цветных маркеров и их позиций для системы рисования.
Ключевые компоненты:
import cv2
import numpy as np
# Конфигурация цветов
myColors = [
[5, 107, 0, 19, 255, 255, "Orange"], # [Hmin, Smin, Vmin, Hmax, Smax, Vmax, Name]
[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(imgResult, (cx, cy), 15, myColorValues[i], cv2.FILLED)
newPoints.append([cx, cy, i])
return newPoints
def drawOnCanvas(myPoints, myColorValues):
for point in myPoints:
cv2.circle(imgResult, (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(img, myColors, myColorValues)
if newPoints:
for point in newPoints:
if myPoints:
last_point = myPoints[-1]
if point[2] == last_point[2]:
distance = np.sqrt((point[0] - last_point[0])**2 +
(point[1] - last_point[1])**2)
if distance < 100:
myPoints.append(point)
else:
myPoints.append(point)
if myPoints:
drawOnCanvas(myPoints, myColorValues)
cv2.putText(imgResult, "Virtual Paint", (10, 50),
cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 3)
cv2.putText(imgResult, "Press 'c' to clear", (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) отсекает мелкие объекты
- Сглаживание траектории предотвращает резкие скачки при рисовании
- Управление с клавиатуры: 'c' для очистки, 'q' для выхода
К чек-листу
Суть задачи: Улучшить качество рисования и добавить дополнительные функции.
Оптимизации:
- Сглаживание траектории: Использование скользящего среднего для плавного рисования
- Адаптивная чувствительность: Изменение параметров детектирования в зависимости от освещения
- Фильтрация дрожания: Удаление аномальных значений позиций
- Управление толщиной линии: Изменение диаметра кисти
Дополнительные функции:
Важные особенности:
- Разделение холста и изображения камеры позволяет сохранять качество рисунка
- Плавное рисование с линиями вместо отдельных точек создает непрерывные линии
- Наложение холста с помощью битовых операций создает эффект рисования на изображении
К чек-листу
Суть проекта: Создать систему сканирования документов с коррекцией перспективы для получения изображения вида "сверху".
Требования к системе:
- Автоматическое детектирование документа на изображении
- Коррекция перспективы для получения прямоугольного вида
- Улучшение качества изображения для лучшей читаемости
- Работа с документами разного формата и ориентации
Теоретическая основа:
- Перспективное преобразование: Математическое преобразование, корректирующее искажение перспективы
- Гомография: Проективное преобразование между двумя плоскостями
- Алгоритм обнаружения углов: Поиск характерных точек документа
- Адаптивная пороговая обработка: Улучшение контрастности для лучшего детектирования
К чек-листу
Суть задачи: Подготовить изображение для детектирования документа с помощью серии последовательных преобразований.
Используемые методы:
- Конвертация в градации серого (
cv2.cvtColor)
- Размытие по Гауссу (
cv2.GaussianBlur)
- Детектирование границ (
cv2.Canny)
- Дилатация (
cv2.dilate)
- Эрозия (
cv2.erode)
Практический пример:
Важные особенности:
- Последовательность операций критична: сначала размытие, потом детектирование границ
- Размер ядра
(5, 5) подходит для большинства документов
- Пороги Canny
(200, 200) обеспечивают детектирование только значимых границ
К чек-листу
Суть задачи: Найти контур документа и определить его угловые точки для перспективного преобразования.
Практический пример:
Важные особенности:
- Фильтрация по площади (
area > 5000) отсекает мелкие объекты
- Проверка на четырехугольник (
len(approx) == 4) гарантирует детектирование документа
- Правильная сортировка углов критична для корректного перспективного преобразования
К чек-листу
Суть задачи: Применить перспективное преобразование для получения вида документа "сверху" и улучшить качество изображения.
Полный код проекта:
import cv2
import numpy as np
widthImg = 640
heightImg = 480
def preProcessing(img):
img = cv2.resize(img, (widthImg, heightImg))
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
imgBlur = cv2.GaussianBlur(imgGray, (5, 5), 1)
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
def getContours(img, imgOriginal):
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)
cv2.drawContours(imgWithContours, [pts], -1, (255, 0, 255), 10)
return pts, imgWithContours
return biggest, imgWithContours
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)
cap.set(10, 150)
docScanMode = False
resultImage = None
while True:
success, img = cap.read()
if not success:
break
img = cv2.resize(img, (widthImg, heightImg))
imgOriginal = img.copy()
imgProcessed = preProcessing(img)
if docScanMode:
biggest, imgWithContours = getContours(imgProcessed, imgOriginal)
if biggest.size != 0:
imgWarped = getWarp(imgOriginal, biggest)
imgEnhanced = enhanceDocument(imgWarped)
imgStack = stackImages(0.6, ([imgOriginal, imgProcessed, imgWithContours],
[imgWarped, imgEnhanced, np.zeros_like(img)]))
resultImage = imgEnhanced.copy()
else:
imgStack = stackImages(0.6, ([imgOriginal, imgProcessed, imgWithContours],
[np.zeros_like(img), np.zeros_like(img), np.zeros_like(img)]))
else:
if resultImage is not None:
imgStack = stackImages(0.8, ([imgOriginal, resultImage],
[np.zeros_like(img), np.zeros_like(img)]))
else:
imgStack = stackImages(0.8, ([imgOriginal, imgProcessed],
[np.zeros_like(img), np.zeros_like(img)]))
cv2.putText(imgStack, f"Mode: {'Scanning' if docScanMode else 'Viewing'}",
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.putText(imgStack, "Press 's' to scan / 'v' to view / 'q' to quit",
(10, heightImg - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
cv2.imshow("Document Scanner", imgStack)
key = cv2.waitKey(1)
if key == ord('q'):
break
elif key == ord('s'):
docScanMode = True
elif key == ord('v'):
docScanMode = False
elif key == ord('r'):
resultImage = None
cap.release()
cv2.destroyAllWindows()
Важные особенности:
- Интерактивный интерфейс с переключением режимов сканирования и просмотра
- Автоматическое детектирование документа без ручного указания углов
- Постобработка с адаптивной пороговой обработкой улучшает читаемость текста
- Обрезка краев после перспективного преобразования удаляет артефакты
Практические рекомендации:
- Для лучшего качества размещайте документ на контрастном фоне
- Обеспечьте равномерное освещение для минимизации теней
- Держите камеру перпендикулярно документу для минимизации искажений
К чек-листу
В этих уроках мы рассмотрели два практических проекта на основе OpenCV:
- Виртуальная рисовальная доска - демонстрирует детектирование цветов, трекинг объектов и интерактивное рисование
- Сканер документов - показывает применение контурного анализа, перспективных преобразований и улучшения качества изображений
Эти проекты иллюстрируют реальное применение компьютерного зрения в повседневных задачах и предоставляют основу для дальнейшего развития навыков в этой области. Следующий урок будет посвящен третьему проекту - распознаванию номеров транспортных средств.
К чек-листу