Я работал над подобной проблемой раньше. Я работаю с C ++ реализацией OpenCV, но у меня есть несколько советов для вас.
Сегментирование статьи
Чтобы добиться лучшей сегментации, попробуйте Изображение Квантование . Этот метод сегментирует изображение в N кластеров , то есть группирует пиксели схожих цветов в группу. Затем эта группа представлена одним цветом.
Преимущество этого метода перед другими, скажем, чистым двоичным порогом 1014 *, состоит в том, что он может идентифицировать несколько распределений цветов - те, которые будут сгруппированы в N кластеров . Проверьте это (извините за ссылки, мне не разрешено размещать прямые изображения):
Результат сегментации изображения
Это поможет вам получить лучшая сегментация вашей бумаги. В реализации используется алгоритм кластеризации, известный как «K-means» (подробнее об этом позже). В моем примере я попробовал 3 кластера и 5 алгоритмов «запуска» (или попыток, поскольку K-means часто выполняется более одного раза).
cv::Mat imageQuantization( cv::Mat inputImage, int numberOfClusters = 3, int iterations = 5 ){
//step 1 : map the src to the samples
cv::Mat samples(inputImage.total(), 3, CV_32F);
auto samples_ptr = samples.ptr<float>(0);
for( int row = 0; row != inputImage.rows; ++row){
auto src_begin = inputImage.ptr<uchar>(row);
auto src_end = src_begin + inputImage.cols * inputImage.channels();
//auto samples_ptr = samples.ptr<float>(row * src.cols);
while(src_begin != src_end){
samples_ptr[0] = src_begin[0];
samples_ptr[1] = src_begin[1];
samples_ptr[2] = src_begin[2];
samples_ptr += 3; src_begin +=3;
}
}
//step 2 : apply kmeans to find labels and centers
int clusterCount = numberOfClusters; //Number of clusters to split the set by
cv::Mat labels;
int attempts = iterations; //Number of times the algorithm is executed using different initial labels
cv::Mat centers;
int flags = cv::KMEANS_PP_CENTERS;
cv::TermCriteria criteria = cv::TermCriteria( CV_TERMCRIT_ITER | CV_TERMCRIT_EPS,
10, 0.01 );
//the call to kmeans:
cv::kmeans( samples, clusterCount, labels, criteria, attempts, flags, centers );
//step 3 : map the centers to the output
cv::Mat clusteredImage( inputImage.size(), inputImage.type() );
for( int row = 0; row != inputImage.rows; ++row ){
auto clusteredImageBegin = clusteredImage.ptr<uchar>(row);
auto clusteredImageEnd = clusteredImageBegin + clusteredImage.cols * 3;
auto labels_ptr = labels.ptr<int>(row * inputImage.cols);
while( clusteredImageBegin != clusteredImageEnd ){
int const cluster_idx = *labels_ptr;
auto centers_ptr = centers.ptr<float>(cluster_idx);
clusteredImageBegin[0] = centers_ptr[0];
clusteredImageBegin[1] = centers_ptr[1];
clusteredImageBegin[2] = centers_ptr[2];
clusteredImageBegin += 3; ++labels_ptr;
}
}
//return the output:
return clusteredImage;
}
Обратите внимание, что алгоритм также создает две дополнительные матрицы. «Метки» - фактические пиксели, помеченные целым числом, которое идентифицирует их кластер. «Центры» - это средние значения для каждого кластера.
Определение краев
Теперь это тривиально запустите Edge Detector для этого сегментированного изображения. Давайте попробуем Кэнни. Параметры, конечно же, могут быть отрегулированы вами. Здесь я попробовал нижний порог 0f 30 и верхний порог из 90. Довольно стандартный, просто убедитесь, что верхний порог соответствует условию = 3 * LowerThreshold, согласно Canny предложения. Вот результат:
Результат обнаружения края
cv::Mat testEdges;
float lowerThreshold = 30;
float upperThreshold = 3 * lowerThreshold;
cv::Canny( testSegmented, testEdges, lowerThreshold, upperThreshold );
Обнаружение линий
Хорошо. Хотите обнаружить линии, произведенные детектором края? Здесь есть как минимум 2 варианта. Первое и самое простое: используйте детектор линии Хафа . Однако, как вы наверняка видели, настройка Hough для поиска линий, которые вы действительно ищете, может быть сложной.
Одним из возможных решений для фильтрации строк, возвращаемых Hough, является запуск «углового фильтра». , так как мы ищем только (близко к) вертикальные и горизонтальные линии. Вы также можете фильтровать строки по длине.
Этот фрагмент кода дает представление о том, что вам нужно реализовать фильтр: // Запустите детектор строк Hough: cv :: HoughLinesP (grad, linesP, 1, CV_PI / 180, minVotes, minLineLength, maxLineGap);
// Process the points (lines)
for( size_t i = 0; i < linesP.size(); i++ ) //points are stored in linesP
{
//get the line
cv::Vec4i l = linesP[i]; //get the line
//get the points:
cv::Point startPoint = cv::Point( l[0], l[1] );
cv::Point endPoint = cv::Point( l[2], l[3] );
//filter horizontal & vertical:
float dx = abs(startPoint.x - endPoint.x);
float dy = abs(startPoint.y - endPoint.y);
//angle filtering, delta y and delta x
if ( (dy < maxDy) || (dx < maxDx) ){
//got my target lines!
}
}
В приведенном выше коде я фактически работаю с линейными компонентами, а не с углами. Итак, мои «угловые» ограничения определяются 2 минимальной длиной компонента : maxDy - максимальной длиной «дельты» по оси y, а также maxDx для оси х.
Другое решение для обнаружения линий состоит в том, чтобы использовать тот факт, что вы просматриваете только линии, у которых УГЛОВ или около 90 градусов между ними. Вы можете запустить морфологический фильтр для обнаружения этих «паттернов» с помощью операции попадания или пропуска :)
В любом случае, возвращаясь к Хау, это обнаружение я получаю без слишком большой настройки параметров и после применения линейного фильтра угла / длины:
Обнаружение линии после фильтрации угла
Охлаждение. Зеленые точки представляют начальную и конечную точки линий. Как видите, их куча. Как мы можем «объединить» их? Что если мы вычислим среднее из этих точек? Хорошо, но мы должны получить среднее значение линий PER «квадрант». Как вы видите на следующем рисунке, я разделил входное изображение на 4 квадранта (желтые линии):
Определение квадранта (желтые линии)
Каждый из Квадранты, к счастью, будут содержать точки, описывающие угол статьи. Для каждого из этих квадрантов проверьте, какие точки попадают в данный квадрант, и вычислите их среднее значение. Это общая идея.
Это достаточно код для написания. К счастью, если мы немного изучим проблему, то увидим, что все зеленые точки имеют тенденцию к CLUSTER в некоторых очень определенных областях (или, как мы говорили ранее, в «квадрантах»). Введите K- означает снова.
K-средства будут группировать данные одинакового значения независимо от того, что. Это могут быть пиксели, это могут быть пространственные точки, это может быть что угодно, просто дайте ему набор данных и количество кластеров, которые вы хотите, и он будет выплевывать найденные кластеры и СРЕДСТВА СКАЗАННЫХ КЛАСТЕРОВ - Ницца!
Если я запускаю K-means с точками линий, возвращенными Hough, я получаю результат, показанный на последнем изображении. Я также отбрасывал очки, которые слишком далеки от среднего. Средство точек возвращается через матрицу "центров", и здесь они отображаются оранжевым цветом - это довольно близко!
Надеюсь, что это вам поможет! :)