Алгоритм определения углов листа бумаги на фото
каков наилучший способ обнаружения углов счета / квитанции / листа бумаги на фотографии? Это должно использоваться для последующей коррекции перспективы, перед 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 ответов:
Я друг Мартина, который работал над этим в начале этого года. Это был мой первый в истории проект по кодированию, и он немного поторопился, поэтому код нуждается в некоторой ошибке...декодирование... Я дам несколько советов из того, что я уже видел, а затем отсортирую свой код в свой выходной день завтра.
первая подсказка,
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()
не идеально, но по крайней мере работает для всех образцов:
вместо того, чтобы начинать с обнаружения края, вы можете использовать обнаружение угла.
Marvin Framework обеспечивает реализацию алгоритма Moravec для этой цели. Вы можете найти углы документов в качестве отправной точки. Ниже вывода алгоритма Моравца:
также вы можете использовать MSER (максимально стабильные экстремальные области) над результатом оператора Собеля, чтобы найти стабильные области изображения. Для каждой области, возвращаемой MSER, вы можете применить выпуклую оболочку и Поли-аппроксимацию, чтобы получить некоторые из них:
но этот вид обнаружения полезен для живого обнаружения больше, чем одно изображение, которое не всегда возвращает лучший результат.
после обнаружения края используйте преобразование Хафа. Затем поместите эти точки в 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);