Как улучшить распознавание банки Coca-Cola: C++ алгоритм

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: почему распознать банку Coca-Cola сложнее, чем кажется

Представьте: вы пишете робота, который должен автоматически сортировать мусор. Или систему, которая считает количество товаров на полке. Одна из самых популярных задач на собеседованиях и в учебных проектах — распознавание банки Coca-Cola. Казалось бы, что сложного? Ярко-красный цилиндр с белой надписью. Но на практике всё оказывается иначе.

На Stack Overflow вопрос "Image Processing: Algorithm Improvement for 'Coca-Cola Can' Recognition" собрал тысячи просмотров и десятки ответов. Программисты бились над проблемой: как надёжно отличить банку от других красных предметов (огнетушитель, красная кружка, кусок пластика) в разных условиях освещения?

В этой статье мы не просто дадим готовый код на C++ с использованием OpenCV. Мы шаг за шагом разберём алгоритмы обработки изображений, которые превращают сырой видеопоток в уверенное распознавание. Вы узнаете, как улучшить точность детекции, избежать ложных срабатываний и написать код, который будет работать в реальном времени.

Проблема: почему простые методы не работают

Типичная ошибка новичка — пытаться найти объект только по цвету. Например, выделить все красные пиксели и найти среди них самый большой контур. Но:

  • Освещение меняется — красный цвет под лампой дневного света и под солнцем выглядит по-разному.
  • Блики на металле — банка отражает свет, и её цвет может казаться белым или оранжевым.
  • Фон шумит — красные футболки, стулья, рекламные щиты сбивают детектор.
  • Форма искажается — банка может быть повёрнута, частично закрыта или деформирована.

Поэтому улучшение алгоритма распознавания — это не просто подбор порогов, а комбинация методов: цветовая фильтрация, поиск контуров, анализ формы и текстур.

Архитектура решения: от пикселей к решению

Мы построим pipeline из трёх этапов:

  1. Предобработка — размытие, перевод в HSV, морфология.
  2. Сегментация — выделение кандидатов по цвету и контурам.
  3. Верификация — фильтрация по соотношению сторон, площади, текстурным признакам.

Такой подход используется в компьютерном зрении C++ для промышленных задач — от сортировки деталей до контроля качества.

Шаг 1: Цветовая фильтрация в HSV

Первый и самый важный шаг — перевести изображение из RGB в HSV (Hue, Saturation, Value). Почему? Потому что HSV отделяет цвет (оттенок) от яркости. Это позволяет задать диапазон для красного цвета, который будет работать при разном освещении.

#include <opencv2/opencv.hpp>
using namespace cv;

Mat hsv, mask;
cvtColor(src, hsv, COLOR_BGR2HSV);

// Диапазон для красного цвета (два интервала из-за цикличности HSV)
Scalar lower_red1 = Scalar(0, 70, 50);
Scalar upper_red1 = Scalar(10, 255, 255);
Scalar lower_red2 = Scalar(170, 70, 50);
Scalar upper_red2 = Scalar(180, 255, 255);

Mat mask1, mask2;
inRange(hsv, lower_red1, upper_red1, mask1);
inRange(hsv, lower_red2, upper_red2, mask2);
mask = mask1 | mask2;

Обратите внимание: красный цвет находится на границе HSV (0 и 180 градусов), поэтому нам нужно два диапазона. Параметры Saturation (70) и Value (50) отсекают слишком бледные или слишком тёмные пиксели.

Шаг 2: Морфология и поиск контуров

После маски мы получаем бинарное изображение, но оно содержит шум. Применяем морфологическое закрытие (closing), чтобы заполнить дырки внутри банки, и открытие (opening), чтобы убрать мелкие точки.

Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
morphologyEx(mask, mask, MORPH_CLOSE, kernel);
morphologyEx(mask, mask, MORPH_OPEN, kernel);

Теперь ищем контуры:

vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(mask, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

Шаг 3: Фильтрация кандидатов

Не каждый красный контур — банка. Вводим геометрические критерии:

  • Площадь — не слишком маленькая, не слишком большая.
  • Соотношение сторон — банка Coca-Cola имеет высоту примерно в 1.2–1.5 раза больше ширины.
  • Степень выпуклости — контур должен быть близок к прямоугольнику со скруглёнными углами.
for (size_t i = 0; i < contours.size(); i++) {
    double area = contourArea(contours[i]);
    if (area < 1000 || area > 50000) continue; // Эмпирические пороги

    RotatedRect rect = minAreaRect(contours[i]);
    float aspect = (float)rect.size.width / rect.size.height;
    if (aspect < 0.6 || aspect > 1.8) continue;

    // Проверка выпуклости: отношение площади контура к площади bounding box
    double convexity = area / (rect.size.width * rect.size.height);
    if (convexity < 0.7) continue;

    // Нашли кандидата — рисуем рамку
    Point2f vertices[4];
    rect.points(vertices);
    for (int j = 0; j < 4; j++)
        line(src, vertices[j], vertices[(j+1)%4], Scalar(0,255,0), 2);
}

Улучшение: как повысить точность до 95%+

Базовый алгоритм работает, но даёт ложные срабатывания на красных цилиндрических объектах. Вот три улучшения из реальных ответов на Stack Overflow:

1. Анализ текстуры с помощью LBP (Local Binary Patterns)

Банка Coca-Cola имеет характерную глянцевую текстуру с надписью. Можно вычислить локальные бинарные шаблоны и сравнить с эталоном. Если гистограмма LBP кандидата близка к эталону банки — это плюс.

// Упрощённый пример: вычисляем среднюю яркость текстуры внутри контура
Mat roi = src(rect.boundingRect());
Scalar mean, stddev;
meanStdDev(roi, mean, stddev);
if (stddev[0] > 30) // Высокая контрастность — признак надписи
    candidate_score++;

2. Использование опорных точек (Feature Matching)

Дет

Блоги

Рекомендаций по книгам