Алгоритм определения углов листа бумаги на фото


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

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

РГБ > серый > края Кэнни детектирования с пороговой обработки > расширения(1) > удалить мелкие предметы(6) > ясно, объектов сноуборд - > выбрать крупнейшими блог основан на выпуклые области. > [обнаружение углов-не реализовано]

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

более широком контексте:

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

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

случай 1 http://madteckhead.com/tmp/IMG_0773_sml.jpg случай 2 http://madteckhead.com/tmp/IMG_0774_sml.jpg случай 3 http://madteckhead.com/tmp/IMG_0775_sml.jpg корпус 4 http://madteckhead.com/tmp/IMG_0776_sml.jpg

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

case 1-canny http://madteckhead.com/tmp/IMG_0773_canny.jpg case 1-post canny http://madteckhead.com/tmp/IMG_0773_postcanny.jpg случай 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 случай 2-крупнейший блог http://madteckhead.com/tmp/IMG_0774_blob.jpg

заранее спасибо за все идеи! Я так люблю!

EDIT: Hough Transform Progress

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

случай 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 83

8 ответов:

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

первая подсказка, OpenCV и python офигенно, двигаться к ним как можно скорее. : D

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

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

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

вы можете сделать анализ параллельных линий или расстояние между линиями гораздо проще.

надеюсь, что это помогает.

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

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

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

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

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()

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

1234

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

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

enter image description here

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

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

result

после обнаружения края используйте преобразование Хафа. Затем поместите эти точки в SVM (supporting vector machine) с их метками, если примеры имеют гладкие линии на них, SVM не будет иметь никаких трудностей для разделения необходимых частей примера и других частей. Мой совет по SVM, поставьте такой параметр, как связность и длина. То есть, если точки соединены и длинные, то они, скорее всего, будут строкой квитанции. Затем вы можете устранить все остальные точки.

здесь у вас есть код @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);
  1. конвертировать в лабораторное пространство

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

  3. затем используйте контуры или hough на одном из кластеров (intenral)