फोटो में पेपर शीट के कोनों का पता लगाने के लिए एल्गोरिदम


98

फोटो में चालान / रसीद / शीट-ऑफ-पेपर के कोनों का पता लगाने का सबसे अच्छा तरीका क्या है? ओसीआर से पहले बाद के परिप्रेक्ष्य सुधार के लिए इसका उपयोग किया जाना है।

मेरा वर्तमान दृष्टिकोण रहा है:

RGB> Grey> कैनी एज डिटेक्शन थ्रेशोल्डिंग के साथ> Dilate (1)> छोटी वस्तुएं निकालें (6)> स्पष्ट बोर्डर ऑब्जेक्ट्स> उत्तल क्षेत्र पर आधारित लार्ज ब्लॉग चुनें। > [कोने का पता लगाने - लागू नहीं]

मैं मदद नहीं कर सकता लेकिन लगता है कि इस प्रकार के विभाजन को संभालने के लिए एक अधिक मजबूत 'बुद्धिमान' / सांख्यिकीय दृष्टिकोण होना चाहिए। मेरे पास बहुत सारे प्रशिक्षण उदाहरण नहीं हैं, लेकिन मैं शायद एक साथ 100 छवियां प्राप्त कर सकता हूं।

व्यापक संदर्भ:

मैं प्रोटोटाइप का उपयोग कर रहा हूं, और OpenCV और टेसरेक्ट-ओसीआर में सिस्टम को लागू करने की योजना बना रहा हूं। यह कई इमेज प्रोसेसिंग समस्याओं में से पहला है, जिन्हें मुझे इस विशिष्ट एप्लिकेशन के लिए हल करना है। इसलिए मैं अपने स्वयं के समाधान को रोल करना चाहता हूं और छवि प्रसंस्करण एल्गोरिदम के साथ खुद को फिर से परिचित करता हूं।

यहाँ कुछ नमूना चित्र दिए गए हैं, जिन्हें मैं एल्गोरिदम को संभालना चाहता हूँ: यदि आप चुनौती लेना चाहते हैं तो बड़ी छवियां http://madteckhead.com/tmp पर हैं

मामला एक
(स्रोत: madteckhead.com )

मामला 2
(स्रोत: madteckhead.com )

मामला 3
(स्रोत: madteckhead.com )

मामला 4
(स्रोत: madteckhead.com )

सबसे अच्छे मामले में यह देता है:

केस 1 - कैनी
(स्रोत: madteckhead.com )

केस 1 - पोस्ट कैनी
(स्रोत: madteckhead.com )

केस 1 - सबसे बड़ा ब्लॉग
(स्रोत: madteckhead.com )

हालांकि यह अन्य मामलों पर आसानी से विफल रहता है:

केस 2 - कैनी
(स्रोत: madteckhead.com )

केस 2 - पोस्ट कैनी
(स्रोत: madteckhead.com )

केस 2 - सबसे बड़ा ब्लॉग
(स्रोत: madteckhead.com )

सभी महान विचारों के लिए अग्रिम धन्यवाद! मैं बहुत प्रेम करता हूँ!

संपादित करें: पर्याप्त परिवर्तन प्रगति

प्रश्न: क्या एल्गोरिथ्म कोनों को खोजने के लिए पर्याप्त लाइनों को क्लस्टर करेगा? उत्तरों की सलाह के बाद मैं Hough Transform का उपयोग करने, लाइनों को चुनने और उन्हें फ़िल्टर करने में सक्षम था। मेरा वर्तमान दृष्टिकोण बल्कि कच्चा है। मैंने मान लिया है कि इनवॉइस हमेशा छवि के साथ संरेखण से 15deg से कम होगी। अगर यह मामला है तो मैं लाइनों के लिए उचित परिणामों के साथ समाप्त होता हूं (नीचे देखें)। लेकिन कोनों के लिए एक्सट्रपलेशन करने के लिए लाइनों (या वोट) को क्लस्टर करने के लिए एक उपयुक्त एल्गोरिथ्म के बारे में पूरी तरह सुनिश्चित नहीं है। कठिन रेखाएं निरंतर नहीं होती हैं। और शोर छवियों में, समानांतर रेखाएं हो सकती हैं इसलिए लाइन मूल मैट्रिक्स से कुछ फार्म या दूरी की आवश्यकता होती है। कोई विचार?

मामला एक मामला 2 मामला 3 मामला 4
(स्रोत: madteckhead.com )


1
हां, मुझे यह लगभग 95% मामलों में काम करने के लिए मिला। मैंने समय की कमी के कारण कोड को रोक दिया है। यदि आप तत्काल मदद की आवश्यकता है, तो मैं कुछ चरणों में एक अनुवर्ती कार्रवाई करूंगा। अच्छे फॉलोवर की कमी के लिए क्षमा करें। मैं इस सुविधा पर काम करना पसंद करूंगा।
नाथन केलर

नाथन, क्या आप इस पर एक अनुवर्ती पोस्ट कर सकते हैं कि आपने इसे कैसे किया? मैं कागजों की शीट के कोनों / आउटटर-समोच्च को पहचानने वाले एक ही बिंदु पर अटक गया हूं। मैं ठीक वैसी ही समस्याओं में भाग लेता हूं जैसा आपने किया था इसलिए मुझे समाधान में बहुत दिलचस्पी होगी।
टिम

6
अब इस पोस्ट में छवियों के सभी 404.
ChrisF

जवाबों:


28

मैं मार्टिन का दोस्त हूं जो इस साल की शुरुआत में काम कर रहा था। यह मेरा अब तक का पहला कोडिंग प्रोजेक्ट था, और थोड़े जल्दबाज़ी में समाप्त हो गया, इसलिए कोड को कुछ इर्र ... डिकोडिंग की आवश्यकता है ... जो मैंने आपको पहले से ही करते देखा है, उससे कुछ टिप्स दूंगा। मेरे कोड को कल मेरे दिन के आधार पर छाँटें।

पहली टिप, OpenCVऔर pythonभयानक हैं, जितनी जल्दी हो सके उनके पास जाएं। : डी

छोटी वस्तुओं और या शोर को हटाने के बजाय, कैनी प्रतिबंधों को कम करें, इसलिए यह अधिक किनारों को स्वीकार करता है, और फिर सबसे बड़ा बंद समोच्च (ओपनसीवी में findcontour()कुछ सरल मापदंडों के साथ उपयोग करें , मुझे लगता है कि मैंने उपयोग किया CV_RETR_LIST)। कागज के एक सफेद टुकड़े पर अभी भी संघर्ष कर सकता है, लेकिन निश्चित रूप से सर्वोत्तम परिणाम प्रदान कर रहा है।

के लिए Houghline2()रूपांतरण, के साथ प्रयास CV_HOUGH_STANDARDकरने के लिए विरोध के रूप में CV_HOUGH_PROBABILISTIC, यह हूँ दे रो और थीटा मूल्यों, ध्रुवीय निर्देशांक में लाइन को परिभाषित करने, और फिर आप समूह कर सकते हैं उन लोगों के लिए एक निश्चित सहिष्णुता के भीतर लाइनों।

मेरा समूहीकरण एक तालिका के रूप में काम करता है, जो प्रत्येक पंक्ति से परिवर्तित होती है, जो इसे एक आरएचओ और थीटा जोड़ी देती है। यदि ये मान भीतर थे, तो तालिका में मानों की एक जोड़ी के 5% का कहना था, उन्हें त्याग दिया गया था, यदि वे उस 5% के बाहर थे, तो तालिका में एक नई प्रविष्टि जोड़ी गई थी।

फिर आप समानांतर लाइनों या लाइनों के बीच की दूरी का विश्लेषण अधिक आसानी से कर सकते हैं।

उम्मीद है की यह मदद करेगा।


हाय डैनियल, शामिल होने के लिए धन्यवाद। मुझे आप पसंद हैं। वास्तव में मार्ग Im पल में अच्छे परिणाम प्राप्त कर रहा है। यहां तक ​​कि और OpenCV उदाहरण भी था जिसमें आयतों का पता चला था। बस परिणामों पर कुछ फ़िल्टरिंग करना था। जैसा कि आप कह रहे थे कि सफेद पर सफेद इस विधि के साथ पता लगाना मुश्किल है। लेकिन यह एक सरल और कम खर्चीला दृष्टिकोण था। मैं वास्तव में मेरे अहंकार से बाहर आ गया है और एक पाली सन्निकटन किया है, opencv में वर्गों के उदाहरण पर एक नज़र है। मुझे आपके मतदान के कार्यान्वयन को देखना है। अग्रिम धन्यवाद, नाथन
नाथन केलर

मैं इस दृष्टिकोण के साथ समस्या कर रहा था, अगर मैं भविष्य के संदर्भ के लिए कुछ बेहतर तैयार कर सकता हूं, तो मैं एक समाधान पोस्ट करूंगा
अंशुमान कुमार

@AshshumanKumar वास्तव में मुझे इस प्रश्न की सहायता की आवश्यकता है, क्या आप मेरी सहायता कर सकते हैं, कृपया? stackoverflow.com/questions/61216402/…
कार्लोस डिएगो

19

मेरे विश्वविद्यालय के एक छात्र समूह ने हाल ही में एक iPhone ऐप (और अजगर OpenCV ऐप) का प्रदर्शन किया है, जो उन्होंने ठीक यही करने के लिए लिखा था। जैसा कि मुझे याद है, कदम कुछ इस तरह थे:

  • कागज पर पाठ को पूरी तरह से हटाने के लिए माध्यियन फिल्टर (यह काफी अच्छे प्रकाश व्यवस्था के साथ श्वेत पत्र पर हस्तलिखित पाठ था और मुद्रित पाठ के साथ काम नहीं कर सकता है, यह बहुत अच्छी तरह से काम करता है)। कारण यह था कि यह कोने का पता लगाने को बहुत आसान बनाता है।
  • लाइनों के लिए पर्याप्त रूपांतरण
  • Hough Transform संचायक स्थान में चोटियों का पता लगाएं और प्रत्येक रेखा को पूरी छवि में खींचें।
  • लाइनों का विश्लेषण करें और उन सभी को हटा दें जो एक-दूसरे के बहुत करीब हैं और एक समान कोण पर हैं (लाइनों को एक में क्लस्टर करें)। यह आवश्यक है क्योंकि Hough Transform सही नहीं है क्योंकि यह असतत नमूना स्पेस में काम कर रहा है।
  • उन पंक्तियों के जोड़े खोजें जो लगभग समानांतर हैं और जो अन्य जोड़ियों को देखने के लिए प्रतिच्छेद करती हैं कि कौन सी रेखाएँ क्वाड बनाती हैं।

ऐसा लगता है कि यह काफी अच्छा काम कर रहा था और वे कागज या किताब के एक टुकड़े की एक तस्वीर लेने में सक्षम थे, कोने का पता लगाने और फिर एक फ्लैट विमान पर छवि में दस्तावेज़ को लगभग वास्तविक समय में मैप करने के लिए (प्रदर्शन करने के लिए एक ही OpenCV फ़ंक्शन था) मानचित्रण)। जब मैंने इसे काम करते देखा तो कोई ओसीआर नहीं था।


महान विचारों के लिए धन्यवाद मार्टिन। Ive ने आपकी सलाह ली और Hough ट्रांसफॉर्म अप्रोच को लागू किया। (ऊपर परिणाम देखें)। मैं एक मजबूत एल्गोरिथ्म निर्धारित करने के लिए संघर्ष कर रहा हूं जो चौराहों को खोजने के लिए लाइनों को एक्सट्रपलेशन करेगा। कई लाइनें नहीं हैं, और कुछ गलत सकारात्मक हैं। क्या आपके पास इस बात की कोई सलाह है कि मैं सबसे अच्छी तरह से विलय कैसे कर सकता हूं? यदि आपके छात्र रुचि रखते हैं, तो कृपया उन्हें संपर्क करने के लिए प्रोत्साहित करें। मैं एल्गोरिदम को मोबाइल प्लेटफ़ॉर्म पर चलाने के लिए उनके अनुभव सुनना पसंद करूंगा। (वह मेरा अगला लक्ष्य है)। आपके विचारों के लिए बहुत धन्यवाद।
नाथन केलर

1
ऐसा लगता है कि लाइनों के लिए HT ने आपकी दूसरी छवि में अच्छी तरह से काम किया है, लेकिन क्या आप संचायक में अपने प्रारंभ और अंत मूल्यों के लिए एक सीमा सहिष्णुता को परिभाषित कर रहे हैं? एचटी वास्तव में स्टार्ट और एंड पोजीशन को परिभाषित नहीं करता है, बल्कि y = mx + c में m और c मान को दर्शाता है। यहां देखें - ध्यान दें कि यह कार्टेशियन के बजाय संचायक में ध्रुवीय निर्देशांक का उपयोग कर रहा है। इस तरह से आप c और फिर m द्वारा उन्हें पतली करने के लिए लाइनों को समूहीकृत कर सकते हैं और पूरी छवि में फैली रेखाओं की कल्पना करके आप अधिक उपयोगी चौराहों पाएंगे।
मार्टिन फुट

@MartinFoot मैं वास्तव में इस सवाल के साथ मदद की ज़रूरत है, क्या आप मेरी मदद कर सकते हैं, कृपया? stackoverflow.com/questions/61216402/…
कार्लोस डिएगो

16

यहाँ मैं प्रयोग के बाद के साथ आया था:

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


4
मैं इसी तरह के प्रोजेक्ट पर काम कर रहा हूं। मैं कोड के ऊपर चलता हूं और यह मुझे "Cv नाम का कोई मॉड्यूल नहीं" त्रुटि देता है। मैंने ओपन सीवी 2.4 संस्करण स्थापित किया है और आयात cv2 मेरे लिए पूरी तरह से काम कर रहा है।
नवनीत सिंह

क्या आप इस कोड को अपडेट करने के लिए पर्याप्त हैं ताकि यह काम करे? pastebin.com/PMH5Y0M8 यह सिर्फ मुझे एक काला पृष्ठ देता है।
7

क्या आपके पास निम्न कोड को जावा में बदलने के बारे में कोई विचार है: 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)
aurelianr

वानुअन मैं वास्तव में इस सवाल के साथ मदद की ज़रूरत है, क्या आप मेरी मदद कर सकते हैं, कृपया? stackoverflow.com/questions/61216402/…
कार्लोस डिएगो

9

एज डिटेक्शन से शुरुआत करने के बजाय आप कॉर्नर डिटेक्शन का इस्तेमाल कर सकते हैं।

मार्विन फ्रेमवर्क इस उद्देश्य के लिए मोरवेक एल्गोरिदम का कार्यान्वयन प्रदान करता है। आप एक प्रारंभिक बिंदु के रूप में कागजात के कोनों को पा सकते हैं। मोरवेक के एल्गोरिदम के आउटपुट के नीचे:

यहां छवि विवरण दर्ज करें


4

इसके अलावा, आप छवि के स्थिर क्षेत्रों को खोजने के लिए सोबेल ऑपरेटर परिणाम पर एमएसईआर ( मैक्सिमली स्टेबल एक्सट्रीम रीजन) का उपयोग कर सकते हैं । MSER द्वारा लौटाए गए प्रत्येक क्षेत्र के लिए आप कुछ इस तरह प्राप्त करने के लिए उत्तल पतवार और पाली सन्निकटन लागू कर सकते हैं:

लेकिन इस तरह का पता लगाना एकल तस्वीर से अधिक लाइव डिटेक्शन के लिए उपयोगी है जो हमेशा सबसे अच्छा परिणाम नहीं देता है।

परिणाम


1
क्या आप इसके लिए कुछ और विवरण साझा कर सकते हैं शायद कुछ कोड, अग्रिम में एक गुच्छा
मोंटी

मुझे cv2.CHAIN_APPROX_SIMPLE में एक त्रुटि प्राप्त हो रही है, जिसमें कहा गया है कि कई मान अनपैक करने के लिए हैं। कोई उपाय? मैं अपने नमूने के रूप में 1024 * 1024 छवि का उपयोग कर रहा हूं
प्रवीण

1
सभी का धन्यवाद, बस वर्तमान Opencv शाखा के उत्तर
प्रवीण

क्या MSER को ब्लब निकालने का मतलब नहीं है? मैंने इसकी कोशिश की और यह केवल पाठ का अधिकांश पता लगाता है
अंशुमान कुमार

3

एज-डिटेक्शन के बाद, Hough Transform का उपयोग करें। फिर, उन बिंदुओं को अपने लेबल के साथ एक SVM (वेक्टर मशीन का समर्थन करते हुए) में रखें, यदि उदाहरणों की उन पर चिकनी रेखाएँ हैं, तो SVM को उदाहरण के आवश्यक भागों और अन्य भागों को विभाजित करने में कोई कठिनाई नहीं होगी। एसवीएम पर मेरी सलाह, कनेक्टिविटी और लंबाई जैसे पैरामीटर डालें। यही है, यदि अंक जुड़े हुए हैं और लंबे हैं, तो वे रसीद की एक पंक्ति होने की संभावना है। फिर, आप अन्य सभी बिंदुओं को समाप्त कर सकते हैं।


हाय एरेस, आपके विचारों के लिए धन्यवाद! मैंने Hough ट्रांस्फ़ॉर्म लागू किया है (ऊपर देखें)। मैं झूठी सकारात्मकता को दिए गए कोनों और गैर निरंतर लाइनों को खोजने के लिए एक मजबूत तरीके से काम नहीं कर सकता। क्या आपके पास कोई और विचार है? जब से मैंने SVM ​​तकनीकों को देखा, कुछ समय हो गया है। क्या यह एक पर्यवेक्षित दृष्टिकोण है? मेरे पास कोई प्रशिक्षण डेटा नहीं है, लेकिन मैं कुछ उत्पन्न कर सकता हूं। मैं एसवीएम के बारे में अधिक जानने के लिए उत्सुक हूं। क्या आप कोई संसाधन सुझा सकते हैं सधन्यवाद। नाथन
नाथन केलर

3

यहाँ आपके पास C ++ का उपयोग करके @Vanuan का कोड है:

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

लाइन्स वेरिएबल की परिभाषा कहां है? Std होना चाहिए: वेक्टर <cv :: Vec4i> लाइनें;
Can'rek

@ Can @rek आप सही हैं। std::vector<cv::Vec4i> lines;मेरी परियोजना में एक वैश्विक दायरे में घोषित किया गया है।
GBF_Gabriel 17

1
  1. प्रयोगशाला स्थान में परिवर्तित करें

  2. किमी के खंड 2 क्लस्टर का उपयोग करें

  3. फिर किसी एक गुच्छे (अंतर्नल) पर आकृति या आटे का उपयोग करें
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.