Fotoğraftaki kağıt yaprağının köşelerini algılayan algoritma


102

Bir fotoğraftaki faturanın / makbuzun / kağıdın köşelerini tespit etmenin en iyi yolu nedir? Bu, OCR'den önce sonraki perspektif düzeltmesi için kullanılacaktır.

Şu anki yaklaşımım:

RGB> Gri> Eşikli Canny Kenar Algılama> Genişlet (1)> Küçük nesneleri kaldır (6)> sınır nesnelerini temizle> Dışbükey Alana göre büyük blogları seçin. > [köşe algılama - Uygulanmadı]

Yardım edemem ama bu tür bir segmentasyonu ele almak için daha sağlam bir 'akıllı' / istatistiksel yaklaşım olması gerektiğini düşünüyorum. Çok fazla eğitim örneğim yok, ancak muhtemelen 100 resmi bir araya getirebilirim.

Daha geniş bağlam:

Matlab'ı prototip oluşturmak için kullanıyorum ve sistemi OpenCV ve Tesserect-OCR'de uygulamayı planlıyorum. Bu, bu özel uygulama için çözmem gereken bir dizi görüntü işleme probleminin ilkidir. Bu yüzden kendi çözümümü hayata geçirmek ve kendimi görüntü işleme algoritmalarına yeniden tanımak istiyorum.

Algoritmanın ele almasını istediğim bazı örnek görseller: Eğer meydan okumayı üstlenmek istiyorsanız, büyük görseller http://madteckhead.com/tmp adresindedir.

dava 1
(kaynak: madteckhead.com )

durum 2
(kaynak: madteckhead.com )

durum 3
(kaynak: madteckhead.com )

durum 4
(kaynak: madteckhead.com )

En iyi durumda bu şunu verir:

durum 1 - canny
(kaynak: madteckhead.com )

durum 1 - canny sonrası
(kaynak: madteckhead.com )

durum 1 - en büyük blog
(kaynak: madteckhead.com )

Ancak diğer durumlarda kolayca başarısız olur:

durum 2 - canny
(kaynak: madteckhead.com )

durum 2 - canny sonrası
(kaynak: madteckhead.com )

durum 2 - en büyük blog
(kaynak: madteckhead.com )

Tüm harika fikirler için şimdiden teşekkürler! SO seviyorum!

DÜZENLEME: Hough Dönüşümü İlerlemesi

S: Köşeleri bulmak için düz çizgileri hangi algoritma kümeler? Cevaplardan alınan tavsiyelere uyarak Hough Dönüşümü'nü kullanabildim, satırları seçip filtreleyebildim. Şu anki yaklaşımım oldukça kaba. Faturanın her zaman resimle uyum açısından 15 dereceden daha az olacağı varsayımını yaptım. Durum böyleyse, satırlar için makul sonuçlar elde ederim (aşağıya bakın). Ancak, köşeleri tahmin etmek için çizgileri kümelemek (veya oylamak) için uygun bir algoritmadan tam olarak emin değilim. Hough hatları sürekli değil. Gürültülü görüntülerde paralel çizgiler olabilir, bu nedenle hat başlangıç ​​ölçülerinden bir miktar biçim veya mesafe gereklidir. Herhangi bir fikir?

dava 1 durum 2 durum 3 durum 4
(kaynak: madteckhead.com )


1
Evet, vakaların yaklaşık% 95'inde işe yaramasını sağladım. O zamandan beri zaman sıkıntısı yüzünden kodu rafa kaldırmak zorunda kaldım. Bir aşamada bir takip göndereceğim, acil yardıma ihtiyacınız olursa beni görevlendirmekten çekinmeyin. İyi bir takip eksikliği için özür dilerim. Bu özellik üzerinde çalışmaya geri dönmeyi çok isterim.
Nathan Keller

Nathan, bunu nasıl yaptığına dair bir yazı yazabilir misin lütfen? Kâğıtların köşelerini / dış hatlarını tanıyarak aynı noktada kaldım. Sizinle aynı problemlerle karşılaşıyorum, bu yüzden bir çözümle çok ilgilenirim.
tim

Yanıtlar:


29

Ben Martin'in bu yılın başlarında bunun üzerinde çalışan arkadaşıyım. Bu benim ilk kodlama projemdi ve biraz aceleyle sona erdi, bu yüzden kodun biraz hata yapması gerekiyor ... kod çözme ... Halihazırda yaptığınızı gördüğüm şeyden birkaç ipucu vereceğim ve sonra kodumu yarın izin günümde sırala.

İlk ipucu OpenCVve pythonharika, onlara mümkün olan en kısa sürede geçin. : D

Küçük nesneleri ve / veya gürültüyü kaldırmak yerine, keskin sınırlamaları azaltın, böylece daha fazla kenar kabul eder ve ardından en büyük kapalı konturu bulun (OpenCV kullanımında findcontour()bazı basit parametrelerle kullandığımı düşünüyorum CV_RETR_LIST). beyaz bir kağıt parçası üzerindeyken hala mücadele edebilirdi, ancak kesinlikle en iyi sonuçları veriyordu.

İçin Houghline2()Transform, deneyin CV_HOUGH_STANDARDaksine CV_HOUGH_PROBABILISTIC, bu olacak vermek rho ve teta değerleri, kutupsal koordinatlarda bir çizgi tanımlaması, ve daha sonra bir grup olabilir kişilerce belirli bir tolerans dahilinde hatları.

Gruplamam bir tablo olarak çalıştı, hough dönüşümünden çıkan her satır için bir rho ve theta çifti verecekti. Bu değerler, örneğin tablodaki bir çift değerin% 5'i dahilindeyse, atılırlar,% 5'in dışındaysa tabloya yeni bir giriş eklenir.

Ardından paralel çizgilerin veya çizgiler arasındaki mesafenin analizini çok daha kolay yapabilirsiniz.

Bu yardımcı olur umarım.


Merhaba Daniel, katıldığınız için teşekkürler. Yaklaşmanızı seviyorum. aslında şu anda iyi sonuçlar aldığım rota. Dikdörtgenleri tespit eden bir OpenCV örneği vardı. Sadece sonuçlar üzerinde biraz filtreleme yapmak zorunda kaldı. derken beyaz üzerine beyazın bu yöntemle tespit edilmesi zordur. Ancak bu, basit ve daha az maliyetli bir yaklaşımdı. Aslında hough yaklaşımını algımın dışında bıraktım ve bir poli yaklaşım yaptım, opencv'deki kareler örneğine bir göz atın. Oylama konusundaki uygulamanızı görmek isterim. Şimdiden teşekkürler, Nathan
Nathan Keller

Bu yaklaşımla ilgili sorunlar yaşıyordum, ileride başvurmak için daha iyi bir şey tasarlayabilirsem bir çözüm yayınlayacağım
Anshuman Kumar

@AnshumanKumar Bu soruda gerçekten yardıma ihtiyacım var, bana yardım edebilir misin lütfen? stackoverflow.com/questions/61216402/…
Carlos Diego

19

Üniversitemdeki bir öğrenci grubu geçtiğimiz günlerde, tam olarak bunu yapmak için yazdıkları bir iPhone uygulamasını (ve python OpenCV uygulamasını) gösterdi. Hatırladığım kadarıyla adımlar şunun gibiydi:

  • Kağıttaki metni tamamen kaldırmak için medyan filtresi (bu, oldukça iyi ışıklandırmaya sahip beyaz kağıt üzerine elle yazılmış metindi ve basılı metinle çalışmayabilir, çok iyi çalıştı). Nedeni, köşe tespitini çok daha kolay hale getirmesiydi.
  • Çizgiler için Hough Dönüşümü
  • Hough Transform biriktirici alanındaki zirveleri bulun ve her çizgiyi tüm görüntü boyunca çizin.
  • Çizgileri analiz edin ve birbirine çok yakın olan ve benzer açıda olanları kaldırın (çizgileri bir araya toplayın). Bu gereklidir çünkü Hough Dönüşümü, ayrık bir örnekleme alanında çalıştığı için mükemmel değildir.
  • Hangi çizgilerin dörtlü oluşturduğunu görmek için kabaca paralel olan ve diğer çiftlerle kesişen çizgi çiftlerini bulun.

Bu oldukça iyi çalışıyor gibi görünüyordu ve bir kağıt veya kitabın fotoğrafını çekebildiler, köşe algılamayı gerçekleştirebildiler ve ardından görüntüdeki belgeyi neredeyse gerçek zamanlı olarak düz bir düzlemde eşleştirebildiler (gerçekleştirmek için tek bir OpenCV işlevi vardı. eşleme). Çalıştığını gördüğümde OCR yoktu.


Harika fikirler için teşekkürler Martin. Tavsiyenizi aldım ve Hough dönüşümü yaklaşımını uyguladım. (Yukarıdaki sonuçlara bakın). Kavşakları bulmak için çizgileri tahmin edecek sağlam bir algoritma belirlemek için uğraşıyorum. Çok fazla satır yok ve birkaç yanlış pozitif var. Satırları en iyi nasıl birleştirip atabileceğim konusunda herhangi bir tavsiyen var mı? Öğrencileriniz ilgileniyorsa, lütfen onları iletişime geçmeye teşvik edin. Algoritmaları bir mobil platformda çalıştırma konusundaki deneyimlerini duymak isterim. (Bu bir sonraki hedefim). Fikirleriniz için çok teşekkürler.
Nathan Keller

1
Görünüşe göre çizgiler için HT, ikinci görüntünüz dışında iyi çalıştı, ancak toplayıcıdaki başlangıç ​​ve bitiş değerleriniz için bir eşik toleransı mı tanımlıyorsunuz? HT gerçekten başlangıç ​​ve bitiş konumlarını tanımlamaz, bunun yerine y = mx + c'deki m ve c değerlerini tanımlar. Buraya bakın - bunun kartezyen yerine toplayıcıdaki kutupsal koordinatları kullandığını unutmayın. Bu şekilde, çizgileri inceltmek için c ve sonra m ile gruplayabilirsiniz ve çizgilerin tüm görüntü boyunca uzandığını hayal ederek daha kullanışlı kesişimler bulacaksınız.
Martin Foot

@MartinFoot Bu soru için gerçekten yardıma ihtiyacım var, bana yardım eder misin lütfen? stackoverflow.com/questions/61216402/…
Carlos Diego

16

İşte biraz deney yaptıktan sonra bulduğum şey:

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

Mükemmel değil, ancak en azından tüm örnekler için çalışıyor:

1 2 3 4


4
Benzer bir proje üzerinde çalışıyorum. Kodun üzerinde çalışıyorum ve bana "cv adında modül yok" hatası veriyor. Open CV 2.4 sürümünü kurdum ve import cv2 benim için mükemmel çalışıyor.
Navneet Singh

Çalışması için bu kodu günceller misiniz? pastebin.com/PMH5Y0M8 bana sadece siyah bir sayfa veriyor.
the7erm

Aşağıdaki kodu 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)
java'ya

Vanuan Bu soru için gerçekten yardıma ihtiyacım var, bana yardım edebilir misin lütfen? stackoverflow.com/questions/61216402/…
Carlos Diego

9

Kenar algılamadan başlamak yerine Köşe algılamayı kullanabilirsiniz.

Marvin Framework , bu amaç için Moravec algoritmasının bir uygulamasını sağlar. Kağıtların köşelerini bir başlangıç ​​noktası olarak bulabilirsiniz. Moravec algoritmasının çıktısının altında:

görüntü açıklamasını buraya girin


4

Ayrıca , görüntünün kararlı bölgelerini bulmak için Sobel operatör sonucu üzerinden MSER (Maksimum kararlı ekstrem bölgeler) kullanabilirsiniz . MSER tarafından döndürülen her bölge için, aşağıdaki gibi bazılarını elde etmek için dışbükey gövde ve poli yaklaşımı uygulayabilirsiniz:

Ancak bu tür bir algılama, her zaman en iyi sonucu vermeyen tek bir resimden daha çok canlı algılama için yararlıdır.

sonuç


1
Bunun için biraz daha ayrıntı paylaşabilir misiniz, belki biraz kod, önceden bir sürü teşekkürler
Monty

Cv2.CHAIN_APPROX_SIMPLE dosyasında, paketin açılması için çok fazla değer olduğunu söyleyen bir hata alıyorum. Herhangi bir fikir?
Örneğim

1
Herkese teşekkürler, mevcut Opencv şubesindeki sözdizimi değişikliğini buldum cevaplar.opencv.org/question/40329/…
Praveen

MSER, lekeleri çıkarmak için tasarlanmadı mı? Denedim ve yalnızca metnin çoğunu algıladı
Anshuman Kumar

3

Kenar algılamadan sonra Hough Transform'u kullanın. Daha sonra, bu noktaları etiketleriyle birlikte bir SVM'ye (destekleyici vektör makinesi) koyun, eğer örnekler üzerinde düz çizgiler varsa, SVM örneğin gerekli kısımlarını ve diğer kısımları bölmekte zorlanmayacaktır. SVM ile ilgili tavsiyem, bağlantı ve uzunluk gibi bir parametre koyun. Diğer bir deyişle, noktalar birbirine bağlı ve uzunsa, bunlar büyük olasılıkla makbuzun bir satırıdır. Ardından, diğer tüm noktaları ortadan kaldırabilirsiniz.


Merhaba Ares, fikirleriniz için teşekkürler! Hough dönüşümünü uyguladım (yukarıya bakın). Yanlış pozitifler ve sürekli olmayan çizgiler verilen köşeleri bulmanın sağlam bir yolunu bulamam. Başka fikriniz var mı? SVM tekniklerine bakmayalı epey oldu. Bu denetimli bir yaklaşım mı? Herhangi bir eğitim verisine sahip değilim, ancak bazılarını oluşturabilirim. SVM hakkında daha fazla bilgi edinmeye istekli olduğum için yaklaşımı araştırmakla ilgilenirim. Herhangi bir kaynak tavsiye edebilir misin? Saygılarımla. Nathan
Nathan Keller

3

Burada C ++ kullanan @ Vanuan koduna sahipsiniz:

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

Çizgiler değişken tanımı nerede? Std :: vector <cv :: Vec4i> satırları olmalıdır;
Can Ürek

@ CanÜrek Haklısın. std::vector<cv::Vec4i> lines;projemde global bir kapsamda ilan edildi.
GBF_Gabriel

1
  1. Laboratuvar alanına dönüştür

  2. Kmeans segment 2 kümesini kullan

  3. Daha sonra kümelerden birinde konturlar veya hough kullanın (intenral)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.