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




image-processing opencv (6)

  1. प्रयोगशाला अंतरिक्ष में कनवर्ट करें

  2. केमन्स सेगमेंट 2 क्लस्टर का प्रयोग करें

  3. फिर क्लस्टर (अंतराल) में से एक पर समोच्च या आटा का उपयोग करें

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

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

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

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

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

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

यहां कुछ नमूना छवि दी गई है जिन्हें मैं एल्गोरिदम को संभालना चाहता हूं: यदि आप चुनौती लेना चाहते हैं तो बड़ी छवियां 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

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

केस 1 - कैनी http://madteckhead.com/tmp/IMG_0773_canny.jpg केस 1 - पोस्ट कैनी http://madteckhead.com/tmp/IMG_0773_postcanny.jpg केस 1 - सबसे बड़ा ब्लॉग http://madteckhead.com/tmp/ IMG_0773_blob.jpg

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

केस 2 - कैनी http://madteckhead.com/tmp/IMG_0774_canny.jpg केस 2 - पोस्ट कैनी http://madteckhead.com/tmp/IMG_0774_postcanny.jpg केस 2 - सबसे बड़ा ब्लॉग http://madteckhead.com/tmp/ IMG_0774_blob.jpg

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

संपादित करें: आटा ट्रांसफॉर्म प्रगति

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

केस 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


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

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


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


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

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

सही नहीं है, लेकिन कम से कम सभी नमूनों के लिए काम करता है:


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

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

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

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

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

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

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


यहां आपके पास 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);






image-segmentation