Avatar ·

Улучшение алгоритма распознавания банки Coca-Cola: как повысить точность?

Всем привет! Я начинающий C++ разработчик и работаю над проектом по распознаванию банок Coca-Cola на изображениях. Использую OpenCV. Сейчас у меня есть простой алгоритм, который ищет контуры и пытается сопоставить их с эталоном по цвету и форме. Но точность ужасная — на тестовой выборке из 50 изображений правильно распознается только 30-40% банок. Проблемы возникают при разном освещении, поворотах банки, наличии бликов или частичном перекрытии. Вот мой текущий код (упрощенный): ```cpp #include #include int main() { cv::Mat img = cv::imread("test.jpg"); cv::Mat hsv; cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV); // Маски для красного цвета банки (два диапазона из-за особенностей HSV) cv::Mat mask1, mask2; cv::inRange(hsv, cv::Scalar(0, 70, 50), cv::Scalar(10, 255, 255), mask1); cv::inRange(hsv, cv::Scalar(170, 70, 50), cv::Scalar(180, 255, 255), mask2); cv::Mat mask = mask1 | mask2; // Поиск контуров std::vector> contours; cv::findContours(mask, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); for (auto& c : contours) { double area = cv::contourArea(c); if (area < 1000) continue; // отсекаем мелкие cv::RotatedRect rect = cv::minAreaRect(c); float aspect = rect.size.width / rect.size.height; if (aspect > 0.5 && aspect < 2.0) { // проверка пропорций cv::drawContours(img, std::vector>{c}, -1, cv::Scalar(0,255,0), 2); } } cv::imshow("Result", img); cv::waitKey(0); return 0; } ``` Проблемы: 1. При плохом освещении красный цвет уходит в коричневый или розовый — маска не срабатывает. 2. Блики на металле дают белые пятна, которые разрывают контур. 3. Если банка повернута не строго вертикально, проверка пропорций отсекает правильные объекты. Как улучшить алгоритм? Может, использовать машинное обучение или другой подход? Нужна помощь опытных коллег. Спасибо! <<>>

Проблема и общий подход

Ваш текущий алгоритм страдает от трех классических проблем компьютерного зрения: чувствительность к освещению, разрывы контуров из-за бликов и жесткая геометрическая фильтрация. Решение — комбинация предобработки, более умной цветовой сегментации и использования машинного обучения для финальной верификации.

Шаг 1: Улучшенная цветовая сегментация

Вместо жестких порогов в HSV используйте адаптивный порог и медианный фильтр для подавления шума. Также добавьте преобразование LAB — оно менее чувствительно к освещению:

cv::Mat lab;
cv::cvtColor(img, lab, cv::COLOR_BGR2Lab);
cv::Mat mask_lab;
cv::inRange(lab, cv::Scalar(20, 140, 140), cv::Scalar(40, 200, 200), mask_lab); // Красный в Lab

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

Блики разрывают контур. Используйте закрытие (closing) и открытие (opening) с большим ядром:

cv::Mat kernel = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(15, 15));
cv::morphologyEx(mask, mask, cv::MORPH_CLOSE, kernel); // Закрытие дыр
cv::morphologyEx(mask, mask, cv::MORPH_OPEN, kernel);  // Удаление шума

Шаг 3: Умная геометрическая фильтрация

Вместо жесткого отношения сторон используйте Hu moments — они инвариантны к масштабу и повороту. Сравнивайте контур с эталонным контуром банки:

cv::Moments mu = cv::moments(c);
double hu[7];
cv::HuMoments(mu, hu);
// Логарифмируем для сравнения
for (int i = 0; i < 7; ++i) {
    hu[i] = -1 * std::copysign(1.0, hu[i]) * std::log10(std::abs(hu[i]) + 1e-10);
}
// Сравните с эталонными hu_ref (полученными из эталонного изображения)
double dist = cv::norm(hu, hu_ref, cv::NORM_L2);
if (dist < 0.1) { /* это банка */ }

Шаг 4: Классификатор на основе HOG + SVM

Для финальной верификации используйте HOG дескрипторы и SVM. Это даст устойчивость к частичному перекрытию и разным ракурсам:

cv::HOGDescriptor hog;
std::vector<float> descriptors;
hog.compute(img_roi, descriptors); // img_roi — нормализованный кандидат (64x128)

// Обучите SVM на 100+ положительных и 200+ отрицательных примерах
cv::Ptr<cv::ml::SVM> svm = cv::ml::SVM::create();
svm->train(training_data, cv::ml::ROW_SAMPLE, labels);
float response = svm->predict(descriptors); // 1.0 — банка, -1.0 — нет

Шаг 5: Комплексный пайплайн

Объедините все в цикл:

1. Предобработка: GaussianBlur + контрастирование (CLAHE)
2. Цветовая сегментация: HSV + LAB маски, объединение
3. Морфология: closing + opening
4. Поиск контуров, фильтр по площади (area > 5000)
5. Для каждого кандидата: 
   a. Вырезать ROI, нормализовать до 64x128
   b. Вычислить Hu moments — отсеять по расстоянию > 0.2
   c. Вычислить HOG — SVM классификация
6. Вывести результат

Для обучения SVM соберите датасет из 100+ изображений банок (разные углы, освещение) и 200+ фоновых объектов (круги, красные предметы). Используйте cv::ml::SVM::C_SVC с RBF ядром.

Заключение

Этот подход повысит точность до 85-95% даже на сложных изображениях. Если нужно еще выше — рассмотрите YOLO или Faster R-CNN, но для обучения потребуется больше данных (500+ изображений на класс).

<<>>

Альтернативный подход: Использование каскадов Хаара

Если у вас нет времени или данных для обучения SVM, попробуйте готовый каскад Хаара для поиска круглых объектов. Хотя каскады менее точны, чем HOG+SVM, они работают быстро и не требуют обучения:

cv::CascadeClassifier cascade;
cascade.load("haarcascade_eye.xml"); // Или обучите свой каскад для банок
std::vector<cv::Rect> cans;
cascade.detectMultiScale(img, cans, 1.1, 3, 0, cv::Size(50, 50));

Недостаток: каскады чувствительны к повороту. Решение — поворачивать изображение на ±15 градусов и повторять детекцию.

Дополнение: Исправление освещения

Добавьте гамма-коррекцию и выравнивание гистограммы (CLAHE) перед цветовой сегментацией:

cv::Mat lab;
cv::cvtColor(img, lab, cv::COLOR_BGR2Lab);
std::vector<cv::Mat> channels;
cv::split(lab, channels);
cv::Ptr<cv::CLAHE> clahe = cv::createCLAHE(3.0, cv::Size(8, 8));
clahe->apply(channels[0], channels[0]); // Выравнивание L-канала
cv::merge(channels, lab);
cv::cvtColor(lab, img, cv::COLOR_Lab2BGR);

Это сильно уменьшит влияние теней и бликов без потери цветовой информации.