обнаружение прямоугольного документа с использованием преобразования hough transform opencv android

Я пытаюсь обнаружить прямоугольный документ с помощью opencv 4 android sdk. Сначала я попытался обнаружить это, найдя контуры, но он не работает с многоцветными документами. Вы можете проверить эту ссылку, чтобы получить лучшее представление: обнаружение многоцветного документа с помощью OpenCV4Android

Я много исследовал и обнаружил, что это можно сделать с помощью преобразования houghline, поэтому я использовал следующий способ обнаружения документа:

исходное изображение -> cvtColor -> фильтр GaussianBlur -> растянуть его, чтобы сделать края резче -> применен алгоритм сегментации изображения водораздела -> ловкое обнаружение краев с динамическим порогом otsu -> затем применено преобразование hough line

то, что я сделал для преобразования строки, это:

Imgproc.HoughLinesP(watershedMat, lines, 1, Math.PI / 180, 50, 100, 50);

    List<Line> horizontals = new ArrayList<>();
    List<Line> verticals = new ArrayList<>();
    for (int x = 0; x < lines.rows(); x++)
    {
        double[] vec = lines.get(x, 0);
        double x1 = vec[0],
                y1 = vec[1],
                x2 = vec[2],
                y2 = vec[3];
        Point start = new Point(x1, y1);
        Point end = new Point(x2, y2);
        Line line = new Line(start, end);
        if (Math.abs(x1 - x2) > Math.abs(y1-y2)) {
            horizontals.add(line);
        } else if (Math.abs(x2 - x1) < Math.abs(y2 - y1)){
            verticals.add(line);
        }
    }

и из приведенного выше списка горизонтальных и вертикальных линий я нахожу точки пересечения, как показано ниже:

protected Point computeIntersection (Line l1, Line l2) {
    double x1 = l1._p1.x, x2= l1._p2.x, y1 = l1._p1.y, y2 = l1._p2.y;
    double x3 = l2._p1.x, x4 = l2._p2.x, y3 = l2._p1.y, y4 = l2._p2.y;
    double d = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);

   // double angle = angleBetween2Lines(l1,l2);
    Log.e("houghline","angle between 2 lines = "+angle);
    Point pt = new Point();
    pt.x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / d;
    pt.y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / d;


  return pt;
}

и из этих четырех точек пересечения я рисую линии. Итак, пока я могу обнаружить через него документ. см. изображение ниже:

введите описание изображения здесь

но, когда с документом связаны другие объекты, он также пытается их обнаружить. Я иду сверху вниз по строкам и слева направо, чтобы найти пересечения самого большого прямоугольника. У меня возникают следующие проблемы:

введите описание изображения здесь  введите описание изображения здесь

Как вы можете видеть на изображениях выше, когда на экране появляется другой объект, он тоже его обнаружит. Как определить только документ? и игнорировать другие объекты? Вот мое исходное изображение:

введите описание изображения здесь

Любая помощь будет высоко оценена !! заранее спасибо


person ImLearning    schedule 29.06.2017    source источник
comment
Если документ всегда один и тот же, вы можете использовать детектор (ORB / SIFT / SURF) и дескриптор на обоих изображениях (ваш захваченный и копия документа) и сопоставить дескрипторы. Это наверняка решит вашу проблему. Вы также можете сделать метод HoughLines более строгим, чтобы он находил только линейные сегменты определенной длины и отклонял другие.   -  person Rick M.    schedule 29.06.2017
comment
Я пытаюсь обнаружить разные документы, поэтому, вероятно, не могу использовать SIFT / SURF. Кроме того, я пробовал houghlines с разными значениями rho, threshold. но ничего не работает ..   -  person ImLearning    schedule 30.06.2017
comment
Можете ли вы загрузить исходное изображение? без накладки?   -  person Rick M.    schedule 30.06.2017
comment
привет @ Рик М. Спасибо за ответ. Я обновил исходное изображение   -  person ImLearning    schedule 30.06.2017


Ответы (1)


Общая информация

  • Я использую OpenCV 3.2.0 в Windows 10, однако все упомянутые функции должны быть доступны в 2.4 и / или Android.
  • Я изменил размер изображения для лучшей визуализации. Это не влияет на текущий подход к решению проблемы, однако, если бы мы использовали какое-то обнаружение краев, мы обязательно должны использовать исходный размер изображения.
  • Текущее предоставленное решение использует множество настраиваемых функций (обнаружение цвета LAB, анализ размера контура и т. Д.), Которые здесь невозможно удалить. Если вам нужна помощь в определенных областях, вы, конечно, можете попросить о помощи в комментариях.

Общее наблюдение за проблемой

Есть несколько причин, по которым ваши предыдущие подходы не сработали. Прежде чем мы перейдем к решению, необходимо учесть несколько наблюдений:

  • У вас есть объект, который содержит как более темные, так и более яркие элементы по сравнению с фоном.
  • У вас есть объект, который состоит из довольно разных частей как по яркости, так и по цвету, а также из общей однородности. Фактически, объект разделен на участок, который очень похож на фон.
  • У вас есть фоновые объекты, которые четко отличимы от общего фона (например, черный объект в правом верхнем углу).
  • Объект часто снимается в слегка наклонном ракурсе. Это вызывает перспективное преобразование прямоугольного объекта.

Решение

Принимая во внимание вышеупомянутые наблюдения, я не думаю, что простая пороговая обработка или обнаружение границ дадут какие-либо надежные результаты, особенно если смотреть на различия между разными изображениями одной и той же сцены. В качестве решения я бы предложил определение и классификацию цвета переднего плана и / или фона через цветовое пространство LAB или HSV. Для классификации соответствующих областей следует использовать образцы изображений наиболее ярких цветов. Например. для переднего плана - темный и ярко-красный, а также золотой / желтоватый цвет книги. Фон состоит из довольно однородного сероватого цвета, который можно использовать для его обнаружения. Возможный алгоритм:

  1. Обнаруживайте и классифицируйте передний и задний план в соответствии с цветовым пространством LAB. Используйте разумный порог цветового расстояния (для меня что-то около 8-10% работает в пространстве LAB - пространство AB может работать на 5-7%). Если изменение цвета из-за переменной яркости становится проблемой, переключитесь на независимый от яркости подход (например, просто используйте компоненты AB и игнорируйте компонент L).
  2. Исключите части фона из обнаружения переднего плана (может быть некоторое перекрытие в классификации, поэтому этот порядок предотвратит путаницу).
  3. На оставшемся двоичном изображении примените поиск контура и отбросьте контуры со слишком маленькими участками.
  4. Остальные контуры образуют книгу. Создайте выпуклую оболочку, которую вы можете использовать в качестве объекта ROI.

Преимущества:

  • Очень точный
  • Работает в нескольких сценариях (изменение фона, разное освещение - если используется правильное цветовое пространство)

Недостатки:

  • Сложно реализовать для новичка (знание LAB или HSV, цветовых расстояний, поддержка многоцветной классификации и т. Д.)
  • Обнаружение цвета полностью зависит от фона и переднего плана. Это означает, что если книга изменится и, например, синий, образцы изображений необходимо адаптировать.
  • Этот подход не сработает, если вся верхняя, нижняя или боковые стороны книги выглядят как фон. В этом случае эти части книги будут классифицированы как фоновые.

Сложность общего решения

Существуют причины, по которым нынешний подход, хотя и продвинутый, не подходит для общего применения (различные книги, различный опыт и т. Д.).

Если вам нужна универсальная система, которая может автоматически обнаруживать разные книги с разным фоном, у вас могут возникнуть проблемы. Это достигает уровня сложности, который будет трудно решить. Это как бы напоминает мне обнаружение номерных знаков: переменная освещенность, шум, окрашенные объекты, сильно различающийся фон, плохой контраст и т. Д. И даже если вы справитесь с этим, вот загвоздка: такая система будет работать только для определенных типов номерных знаков. То же самое и с вашими книгами.

Тесты

Поскольку вы разместили очень похожий вопрос (обнаружение многоцветного документа с помощью OpenCV4Android), Я взял на себя смелость использовать изображение, размещенное там, а также те, которые вы предоставили здесь. Поскольку одно из изображений было доступно только с красной областью интереса, я использовал свой уровень навыков Photoshop> 9000, чтобы удалить красную область интереса :).

Примеры изображений для классификации фона

b

Примеры изображений для классификации переднего плана

23 4

Изображения

56 7

Фоновая классификация

89 10

Классификация переднего плана

1112 13

Обнаруженные объекты

89 10



Обновлять

Быстрый курс 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 и пороговым значением цветового расстояния - Как цвета объектов могут изменяться при переменном освещении условия - Как расстояние до других цветов становится больше / тем меньше / тем светлее / тем светлее становится изображение (это также становится ясно, если вы внимательно прочитаете размещенную ссылку).

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

Код и изображения

1718

(изображения взяты из 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
comment
Спасибо, приятель, за твои усилия. Но я новичок в opencv и понятия не имею о LAB. Может быть, вы поможете мне с примером кода для применения LAB !! - person ImLearning; 10.07.2017
comment
@ImLearning Я добавил пример кода, а также несколько пояснений. Я думаю, что, поиграв с примером и прочитав внешнее руководство (и другие доступные материалы), вы должны хорошо понять тему. - person Baiz; 11.07.2017
comment
Я еще не проверял ваш код, но я ценю ваши усилия, приятель. - person ImLearning; 11.07.2017