Общая информация
- Я использую OpenCV 3.2.0 в Windows 10, однако все упомянутые функции должны быть доступны в 2.4 и / или Android.
- Я изменил размер изображения для лучшей визуализации. Это не влияет на текущий подход к решению проблемы, однако, если бы мы использовали какое-то обнаружение краев, мы обязательно должны использовать исходный размер изображения.
- Текущее предоставленное решение использует множество настраиваемых функций (обнаружение цвета LAB, анализ размера контура и т. Д.), Которые здесь невозможно удалить. Если вам нужна помощь в определенных областях, вы, конечно, можете попросить о помощи в комментариях.
Общее наблюдение за проблемой
Есть несколько причин, по которым ваши предыдущие подходы не сработали. Прежде чем мы перейдем к решению, необходимо учесть несколько наблюдений:
- У вас есть объект, который содержит как более темные, так и более яркие элементы по сравнению с фоном.
- У вас есть объект, который состоит из довольно разных частей как по яркости, так и по цвету, а также из общей однородности. Фактически, объект разделен на участок, который очень похож на фон.
- У вас есть фоновые объекты, которые четко отличимы от общего фона (например, черный объект в правом верхнем углу).
- Объект часто снимается в слегка наклонном ракурсе. Это вызывает перспективное преобразование прямоугольного объекта.
Решение
Принимая во внимание вышеупомянутые наблюдения, я не думаю, что простая пороговая обработка или обнаружение границ дадут какие-либо надежные результаты, особенно если смотреть на различия между разными изображениями одной и той же сцены. В качестве решения я бы предложил определение и классификацию цвета переднего плана и / или фона через цветовое пространство LAB или HSV. Для классификации соответствующих областей следует использовать образцы изображений наиболее ярких цветов. Например. для переднего плана - темный и ярко-красный, а также золотой / желтоватый цвет книги. Фон состоит из довольно однородного сероватого цвета, который можно использовать для его обнаружения. Возможный алгоритм:
- Обнаруживайте и классифицируйте передний и задний план в соответствии с цветовым пространством LAB. Используйте разумный порог цветового расстояния (для меня что-то около 8-10% работает в пространстве LAB - пространство AB может работать на 5-7%). Если изменение цвета из-за переменной яркости становится проблемой, переключитесь на независимый от яркости подход (например, просто используйте компоненты AB и игнорируйте компонент L).
- Исключите части фона из обнаружения переднего плана (может быть некоторое перекрытие в классификации, поэтому этот порядок предотвратит путаницу).
- На оставшемся двоичном изображении примените поиск контура и отбросьте контуры со слишком маленькими участками.
- Остальные контуры образуют книгу. Создайте выпуклую оболочку, которую вы можете использовать в качестве объекта ROI.
Преимущества:
- Очень точный
- Работает в нескольких сценариях (изменение фона, разное освещение - если используется правильное цветовое пространство)
Недостатки:
- Сложно реализовать для новичка (знание LAB или HSV, цветовых расстояний, поддержка многоцветной классификации и т. Д.)
- Обнаружение цвета полностью зависит от фона и переднего плана. Это означает, что если книга изменится и, например, синий, образцы изображений необходимо адаптировать.
- Этот подход не сработает, если вся верхняя, нижняя или боковые стороны книги выглядят как фон. В этом случае эти части книги будут классифицированы как фоновые.
Сложность общего решения
Существуют причины, по которым нынешний подход, хотя и продвинутый, не подходит для общего применения (различные книги, различный опыт и т. Д.).
Если вам нужна универсальная система, которая может автоматически обнаруживать разные книги с разным фоном, у вас могут возникнуть проблемы. Это достигает уровня сложности, который будет трудно решить. Это как бы напоминает мне обнаружение номерных знаков: переменная освещенность, шум, окрашенные объекты, сильно различающийся фон, плохой контраст и т. Д. И даже если вы справитесь с этим, вот загвоздка: такая система будет работать только для определенных типов номерных знаков. То же самое и с вашими книгами.
Тесты
Поскольку вы разместили очень похожий вопрос (обнаружение многоцветного документа с помощью OpenCV4Android), Я взял на себя смелость использовать изображение, размещенное там, а также те, которые вы предоставили здесь. Поскольку одно из изображений было доступно только с красной областью интереса, я использовал свой уровень навыков Photoshop> 9000, чтобы удалить красную область интереса :).
Примеры изображений для классификации фона
Примеры изображений для классификации переднего плана
Изображения
Фоновая классификация
Классификация переднего плана
Обнаруженные объекты
Обновлять
Быстрый курс LAB
Поскольку теория цветовых пространств довольно обширна, вам следует сначала ознакомиться с некоторыми основами и ключевыми моментами. Мой быстрый поиск нашел этот сайт, который хорошо объясняет некоторые важные моменты: http://www.learnopencv.com/color-spaces-in-opencv-cpp-python/ - мы будем использовать вариант OpenCV с плавающей запятой, так как он самый простой в использовании (неизменный диапазон LAB, без масштабирования, без подгонки , так далее.). - Диапазон значений LAB: ось L * (яркость) колеблется от 0 до 100, а ось a * и b * (атрибуты цвета) - от -128 до +127. Источники и ссылки: Каковы диапазоны координат в цветовом пространстве CIELAB? http://www.colourphil.co.uk/lab_lch_colour_space.shtml
Цветовое расстояние
https://en.wikipedia.org/wiki/Color_difference
По сути, мы используем евклидово расстояние между двумя цветами. Конечно, мы можем опустить компоненты из двух сравниваемых цветов, например компонент яркости (L).
Чтобы получить интуитивно понятную метрику цветового расстояния, мы можем просто нормализовать цветовые расстояния до диапазона от 0,0 до 1,0. Таким образом, мы можем интерпретировать цветовые расстояния как отклонение в процентах.
Пример
Давайте воспользуемся изображениями со страницы руководства, размещенной выше, и воспользуемся ими в качестве примера. Пример приложения показывает следующее: - Преобразование BGR в LAB - (L) Расчет расстояния AB - (L) Нормализация расстояния AB - Классификация цветов в соответствии со значениями BGR / LAB и пороговым значением цветового расстояния - Как цвета объектов могут изменяться при переменном освещении условия - Как расстояние до других цветов становится больше / тем меньше / тем светлее / тем светлее становится изображение (это также становится ясно, если вы внимательно прочитаете размещенную ссылку).
Дополнительный совет: пример должен показать, что одного цвета часто недостаточно для обнаружения цветных объектов в сильно меняющихся условиях освещения. Решением может быть использование разных пороговых значений цветового расстояния для каждого цвета путем эмпирического анализа. Альтернативой является использование множества образцов цветов классификации для каждого цвета, который вы хотите найти. Вам нужно будет вычислить цветовое расстояние для каждого из этих образцов цветов и объединить найденные значения, сложив результаты с помощью логической операции ИЛИ.
Код и изображения
(изображения взяты из http://www.learnopencv.com/color-spaces-in-opencv-cpp-python/ - учебник Сати Маллик)
#include <opencv2/opencv.hpp>
// Normalization factors for (L)AB distance calculation
// LAB range:
// L: 0.0 - 100.0
// A: -128.0 - 127.0
// B: -128.0 - 127.0
static const float labNormalizationFactor = (float)(1.f / (std::sqrt(std::pow(100, 2) + std::pow(255, 2) + std::pow(255, 2))));
static const float abNormalizationFactor = (float)(1.f / (std::sqrt(std::pow(255, 2) + std::pow(255, 2))));
float labExample_calculateLabDistance(const cv::Vec3f& c1, const cv::Vec3f& c2)
{
return (float)cv::norm(c1, c2) * labNormalizationFactor;
}
float labExample_calculateAbDistance(const cv::Vec3f& c1, const cv::Vec3f& c2)
{
cv::Vec2f c1Temp(c1(1), c1(2));
cv::Vec2f c2Temp(c2(1), c2(2));
return (float)cv::norm(c1Temp, c2Temp) * abNormalizationFactor;
}
void labExample_calculateLabDistance(
cv::Mat& imgLabFloat,
cv::Mat& distances,
const cv::Vec3f labColor,
const bool useOnlyAbDistance
)
{
// Get size for general usage
const auto& size = imgLabFloat.size();
distances = cv::Mat::zeros(size, CV_32F);
distances = 1.f;
for (int y = 0; y < size.height; ++y)
{
for (int x = 0; x < size.width; ++x)
{
// Read LAB value
const auto& value = imgLabFloat.at<cv::Vec3f>(y,x);
// Calculate distance
float distanceValue;
if (useOnlyAbDistance)
{
distanceValue = labExample_calculateAbDistance(value, labColor);
}
else
{
distanceValue = labExample_calculateLabDistance(value, labColor);
}
distances.at<float>(y,x) = distanceValue;
}
}
}
// Small hacky function to convert a single
// BGR color value to LAB float.
// Since the conversion function is not directly available
// we just use a Mat object to do the conversion.
cv::Vec3f labExample_bgrUchar2LabFloat(const cv::Scalar bgr)
{
// Build Mat with single bgr pixel
cv::Mat matWithSinglePixel = cv::Mat::zeros(1, 1, CV_8UC3);
matWithSinglePixel.setTo(bgr);
// Convert to float and scale accordingly
matWithSinglePixel.convertTo(matWithSinglePixel, CV_32FC3, 1.0 / 255.0);
// Convert to LAB and return value
cv::cvtColor(matWithSinglePixel, matWithSinglePixel, CV_BGR2Lab);
auto retval = matWithSinglePixel.at<cv::Vec3f>(0, 0);
return retval;
}
void labExample_convertImageBgrUcharToLabFloat(cv::Mat& src, cv::Mat& dst)
{
src.convertTo(dst, CV_32FC3, 1.0 / 255.0);
cv::cvtColor(dst, dst, CV_BGR2Lab);
}
void labExample()
{
// Load image
std::string path = "./Testdata/Stackoverflow lab example/";
std::string filename1 = "1.jpg";
std::string fqn1 = path + filename1;
cv::Mat img1 = cv::imread(fqn1, cv::IMREAD_COLOR);
std::string filename2 = "2.jpg";
std::string fqn2 = path + filename2;
cv::Mat img2 = cv::imread(fqn2, cv::IMREAD_COLOR);
// Combine images by scaling the second image so both images have the same number of columns and then combining them.
float scalingFactorX = (float)img1.cols / img2.cols;
float scalingFactorY = scalingFactorX;
cv::resize(img2, img2, cv::Size(), scalingFactorX, scalingFactorY);
std::vector<cv::Mat> mats;
mats.push_back(img1);
mats.push_back(img2);
cv::Mat img;
cv::vconcat(mats, img);
// Lets use some reference colors.
// Remember: OpenCV uses BGR as default color space so all colors
// are BGR by default, too.
cv::Scalar bgrColorRed(52, 42, 172);
cv::Scalar bgrColorOrange(3, 111, 219);
cv::Scalar bgrColorYellow(1, 213, 224);
cv::Scalar bgrColorBlue(187, 95, 0);
cv::Scalar bgrColorGray(127, 127, 127);
// Build LAB image
cv::Mat imgLabFloat;
labExample_convertImageBgrUcharToLabFloat(img, imgLabFloat);
// Convert bgr ref color to lab float.
// INSERT color you want to analyze here:
auto colorLabFloat = labExample_bgrUchar2LabFloat(bgrColorRed);
cv::Mat colorDistancesWithL;
cv::Mat colorDistancesWithoutL;
labExample_calculateLabDistance(imgLabFloat, colorDistancesWithL, colorLabFloat, false);
labExample_calculateLabDistance(imgLabFloat, colorDistancesWithoutL, colorLabFloat, true);
// Color distances. They can differ for every color being analyzed.
float maxColorDistanceWithL = 0.07f;
float maxColorDistanceWithoutL = 0.07f;
cv::Mat detectedValuesWithL = colorDistancesWithL <= maxColorDistanceWithL;
cv::Mat detectedValuesWithoutL = colorDistancesWithoutL <= maxColorDistanceWithoutL;
cv::Mat imgWithDetectedValuesWithL = cv::Mat::zeros(img.size(), CV_8UC3);
cv::Mat imgWithDetectedValuesWithoutL = cv::Mat::zeros(img.size(), CV_8UC3);
img.copyTo(imgWithDetectedValuesWithL, detectedValuesWithL);
img.copyTo(imgWithDetectedValuesWithoutL, detectedValuesWithoutL);
cv::imshow("img", img);
cv::imshow("colorDistancesWithL", colorDistancesWithL);
cv::imshow("colorDistancesWithoutL", colorDistancesWithoutL);
cv::imshow("detectedValuesWithL", detectedValuesWithL);
cv::imshow("detectedValuesWithoutL", detectedValuesWithoutL);
cv::imshow("imgWithDetectedValuesWithL", imgWithDetectedValuesWithL);
cv::imshow("imgWithDetectedValuesWithoutL", imgWithDetectedValuesWithoutL);
cv::waitKey();
}
int main(int argc, char** argv)
{
labExample();
}
person
Baiz
schedule
09.07.2017