Алгоритм определения углов листа бумаги на фото - PullRequest
89 голосов
/ 02 июля 2011

Как лучше всего определить углы счета / квитанции / листа бумаги на фотографии?Это должно использоваться для последующей коррекции перспективы, перед распознаванием.

Мой текущий подход был:

RGB> Серый> Обнаружение канни-края с порогом> Расширить (1)> Удалить мелкие объекты (6)> очистить границы объектов> выбрать большой блог на основе выпуклой области.> [обнаружение угла - не реализовано]

Не могу помочь, но думаю, что должен быть более надежный «интеллектуальный» / статистический подход для обработки этого типа сегментации.У меня не так много обучающих примеров, но я мог бы собрать 100 изображений вместе.

Более широкий контекст:

Я использую matlab для создания прототипа и планирую внедрить систему вOpenCV и Tesserect-OCR.Это первая из многих проблем обработки изображений, которые мне нужно решить для этого конкретного приложения.Поэтому я собираюсь найти свое собственное решение и заново ознакомиться с алгоритмами обработки изображений.

Вот несколько примеров изображений, которые я бы хотел обработать алгоритмом: если вы хотите принять вызов, большие изображения находятся по адресу http://madteckhead.com/tmp

case 1 http://madteckhead.com/tmp/IMG_0773_sml.jpg case 2 http://madteckhead.com/tmp/IMG_0774_sml.jpg case 3 http://madteckhead.com/tmp/IMG_0775_sml.jpg case 4 http://madteckhead.com/tmp/IMG_0776_sml.jpg

В лучшем случае это дает:

case1 - canny http://madteckhead.com/tmp/IMG_0773_canny.jpg case 1 - post canny http://madteckhead.com/tmp/IMG_0773_postcanny.jpg case 1 - самый большой блог http://madteckhead.com/tmp/IMG_0773_blob.jpg

Однако в других случаях это не удается:

case 2- canny http://madteckhead.com/tmp/IMG_0774_canny.jpg case 2 - post canny http://madteckhead.com/tmp/IMG_0774_postcanny.jpg case 2 - крупнейший блог http://madteckhead.com/tmp/IMG_0774_blob.jpg

Заранее благодарим за все замечательные идеи!Я люблю SO!

РЕДАКТИРОВАТЬ: Hough Transform Progress

Q: Какой алгоритм будет кластеризовать линии неровностей, чтобы найти углы?Следуя советам из ответов, я смог использовать преобразование Хафа, выбирать линии и фильтровать их.Мой нынешний подход довольно грубый.Я сделал предположение, что счет всегда будет меньше 15 градусов по сравнению с изображением.Я получаю разумные результаты для строк, если это так (см. Ниже).Но я не совсем уверен в подходящем алгоритме кластеризации линий (или голосования) для экстраполяции по углам.Линии Хаф не являются непрерывными.А на зашумленных изображениях могут быть параллельные линии, поэтому требуется некоторая форма или расстояние от метрик начала линии.Есть идеи?

кейс 1 http://madteckhead.com/tmp/IMG_0773_hough.jpg кейс 2 http://madteckhead.com/tmp/IMG_0774_hough.jpg кейс 3 http://madteckhead.com/tmp/IMG_0775_hough.jpg кейс 4 http://madteckhead.com/tmp/IMG_0776_hough.jpg

Ответы [ 8 ]

26 голосов
/ 11 июля 2011

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

Первый совет, OpenCV и python потрясающие, переходите к ним как можно скорее. : D

Вместо того, чтобы удалять мелкие объекты и / или шум, опустите ограничительные ограничения, чтобы он принимал больше ребер, а затем найдите самый большой замкнутый контур (в OpenCV используйте findcontour() с некоторыми простыми параметрами, я думаю, что я использовал CV_RETR_LIST) , может все еще бороться, когда это на белом листе бумаги, но определенно обеспечивает лучшие результаты.

Для преобразования Houghline2(), попробуйте с CV_HOUGH_STANDARD в отличие от CV_HOUGH_PROBABILISTIC, это даст значения rho и theta , определяющие линию в полярном координаты, а затем вы можете сгруппировать линии в пределах определенного допуска к ним.

Моя группировка работала как справочная таблица, для каждой строки, выведенной из грубого преобразования, она давала бы пару rho и theta. Если эти значения были в пределах, скажем, 5% пары значений в таблице, они отбрасывались, если они находились за пределами этих 5%, в таблицу добавлялась новая запись.

После этого вы сможете гораздо легче анализировать параллельные линии или расстояние между линиями.

Надеюсь, это поможет.

17 голосов
/ 02 июля 2011

Студенческая группа в моем университете недавно продемонстрировала приложение для iPhone (и приложение Python OpenCV), которое они написали именно для этого. Насколько я помню, шаги были примерно такими:

  • Медианный фильтр для полного удаления текста на бумаге (это был рукописный текст на белой бумаге с довольно хорошим освещением и может не работать с печатным текстом, он работал очень хорошо). Причина заключалась в том, что это облегчает обнаружение углов.
  • Преобразование Хафа для линий
  • Найдите пики в пространстве аккумулятора Hough Transform и проведите каждую линию по всему изображению.
  • Проанализируйте линии и удалите все, которые очень близки друг к другу и находятся под одинаковым углом (сгруппируйте линии в одну). Это необходимо, потому что преобразование Хафа не является совершенным, поскольку оно работает в дискретном семпле.
  • Найдите пары линий, которые приблизительно параллельны и пересекают другие пары, чтобы увидеть, какие линии образуют квадраты.

Это, казалось, работало довольно хорошо, и они смогли сфотографировать лист бумаги или книгу, выполнить обнаружение угла и затем отобразить документ на изображении на плоскую плоскость практически в реальном времени (был один OpenCV функция для выполнения сопоставления). Когда я увидел, что это работает, не было никакого распознавания.

16 голосов
/ 20 сентября 2013

Вот что я придумал после небольшого количества экспериментов:

import cv, cv2, numpy as np
import sys

def get_new(old):
    new = np.ones(old.shape, np.uint8)
    cv2.bitwise_not(new,new)
    return new

if __name__ == '__main__':
    orig = cv2.imread(sys.argv[1])

    # these constants are carefully picked
    MORPH = 9
    CANNY = 84
    HOUGH = 25

    img = cv2.cvtColor(orig, cv2.COLOR_BGR2GRAY)
    cv2.GaussianBlur(img, (3,3), 0, img)


    # this is to recognize white on white
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT,(MORPH,MORPH))
    dilated = cv2.dilate(img, kernel)

    edges = cv2.Canny(dilated, 0, CANNY, apertureSize=3)

    lines = cv2.HoughLinesP(edges, 1,  3.14/180, HOUGH)
    for line in lines[0]:
         cv2.line(edges, (line[0], line[1]), (line[2], line[3]),
                         (255,0,0), 2, 8)

    # finding contours
    contours, _ = cv2.findContours(edges.copy(), cv.CV_RETR_EXTERNAL,
                                   cv.CV_CHAIN_APPROX_TC89_KCOS)
    contours = filter(lambda cont: cv2.arcLength(cont, False) > 100, contours)
    contours = filter(lambda cont: cv2.contourArea(cont) > 10000, contours)

    # simplify contours down to polygons
    rects = []
    for cont in contours:
        rect = cv2.approxPolyDP(cont, 40, True).copy().reshape(-1, 2)
        rects.append(rect)

    # that's basically it
    cv2.drawContours(orig, rects,-1,(0,255,0),1)

    # show only contours
    new = get_new(img)
    cv2.drawContours(new, rects,-1,(0,255,0),1)
    cv2.GaussianBlur(new, (9,9), 0, new)
    new = cv2.Canny(new, 0, CANNY, apertureSize=3)

    cv2.namedWindow('result', cv2.WINDOW_NORMAL)
    cv2.imshow('result', orig)
    cv2.waitKey(0)
    cv2.imshow('result', dilated)
    cv2.waitKey(0)
    cv2.imshow('result', edges)
    cv2.waitKey(0)
    cv2.imshow('result', new)
    cv2.waitKey(0)

    cv2.destroyAllWindows()

Не идеально, но по крайней мере работает для всех образцов:

1 2 3 4

8 голосов
/ 25 мая 2013

Вместо того, чтобы начинать с обнаружения края, вы можете использовать определение угла.

Marvin Framework обеспечивает реализацию алгоритма Моравека для этой цели.Вы можете найти углы бумаги в качестве отправной точки.Ниже выводится алгоритм Моравека:

enter image description here

4 голосов
/ 17 августа 2015

Также вы можете использовать MSER (Максимально стабильные экстремальные области) поверх результата оператора Собеля, чтобы найти стабильные области изображения. Для каждой области, возвращаемой MSER, вы можете применить аппроксимацию выпуклой оболочки и поли, чтобы получить примерно такую:

Но этот вид обнаружения полезен для обнаружения в реальном времени не только одного изображения, которое не всегда дает лучший результат.

result

3 голосов
/ 30 сентября 2013

Здесь у вас есть код @Vanuan с использованием C ++:

cv::cvtColor(mat, mat, CV_BGR2GRAY);
cv::GaussianBlur(mat, mat, cv::Size(3,3), 0);
cv::Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Point(9,9));
cv::Mat dilated;
cv::dilate(mat, dilated, kernel);

cv::Mat edges;
cv::Canny(dilated, edges, 84, 3);

std::vector<cv::Vec4i> lines;
lines.clear();
cv::HoughLinesP(edges, lines, 1, CV_PI/180, 25);
std::vector<cv::Vec4i>::iterator it = lines.begin();
for(; it!=lines.end(); ++it) {
    cv::Vec4i l = *it;
    cv::line(edges, cv::Point(l[0], l[1]), cv::Point(l[2], l[3]), cv::Scalar(255,0,0), 2, 8);
}
std::vector< std::vector<cv::Point> > contours;
cv::findContours(edges, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_TC89_KCOS);
std::vector< std::vector<cv::Point> > contoursCleaned;
for (int i=0; i < contours.size(); i++) {
    if (cv::arcLength(contours[i], false) > 100)
        contoursCleaned.push_back(contours[i]);
}
std::vector<std::vector<cv::Point> > contoursArea;

for (int i=0; i < contoursCleaned.size(); i++) {
    if (cv::contourArea(contoursCleaned[i]) > 10000){
        contoursArea.push_back(contoursCleaned[i]);
    }
}
std::vector<std::vector<cv::Point> > contoursDraw (contoursCleaned.size());
for (int i=0; i < contoursArea.size(); i++){
    cv::approxPolyDP(Mat(contoursArea[i]), contoursDraw[i], 40, true);
}
Mat drawing = Mat::zeros( mat.size(), CV_8UC3 );
cv::drawContours(drawing, contoursDraw, -1, cv::Scalar(0,255,0),1);
3 голосов
/ 03 июля 2011

После обнаружения края используйте Hough Transform. Затем поместите эти точки в SVM (поддерживающую векторную машину) с их метками. Если на примерах имеются плавные линии, у SVM не возникнет никаких трудностей разделить необходимые части примера и другие части. Мой совет по SVM, поставьте такой параметр как связность и длина. То есть, если точки связаны и длинны, они, вероятно, будут линией квитанции. Затем вы можете устранить все остальные пункты.

1 голос
/ 29 октября 2014
  1. Преобразовать в лабораторное пространство

  2. Использовать кластер сегмента 2 kmeans

  3. Затем использовать контуры или черту на одном из кластеров (целое)
...