Opencv का उपयोग करके छवि में मौजूद सभी पाठ का स्थान प्राप्त करें


11

मेरी यह छवि है जिसमें इसमें पाठ (संख्याएँ और अक्षर) हैं। मैं इस छवि में मौजूद सभी पाठ और संख्याओं का स्थान प्राप्त करना चाहता हूं। इसके अलावा मैं सभी पाठ भी निकालना चाहता हूं।

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

मैं अपनी छवि के सभी पाठ (संख्या और अक्षर) के साथ-साथ कॉर्डिनेट कैसे प्राप्त कर सकता हूं। उदाहरण के लिए 10B, 44, 16, 38, 22B आदि


आपका टेंसरफ़्लो संस्करण क्या है? यदि आपका संस्करण 2.1 है, तो
गेज़लज़

1
बुरे सवाल पर इनाम फेंकना एक अच्छा अभ्यास नहीं है। आपने ऐसा करने के बारे में कोई ज्ञान नहीं दिखाया, इसलिए ऐसा लगता है कि आप डेवलपर्स को केवल कुछ प्रतिनिधि बिंदुओं के बदले में पूर्ण समाधान कोडित करने का लालच दे रहे हैं। मुझे उस कारण के लिए सही उत्तर देखने की उम्मीद नहीं है, लेकिन मेरा मानना ​​है कि आप फ्रीलान्स वेबसाइटों पर बेहतर समाधान प्राप्त कर सकते हैं यदि आप लोगों को उनके समय का भुगतान करते हैं।
कार्लफिलिप

@Klphillip इतना खेद है, लेकिन मैं एक शुरुआत हूं मुझे शुरू करने के लिए कुछ चाहिए, है ना? क्या आप इसमें मेरी मदद कर सकते हैं
पुलकित भटनागर

जवाबों:


13

यहां गैर-पाठ आकृति को फ़िल्टर करने के लिए रूपात्मक संचालन का उपयोग करके एक संभावित दृष्टिकोण दिया गया है। विचार यह है:

  1. बाइनरी इमेज प्राप्त करें। लोड इमेज, ग्रेस्केल, फिर ओट्सु की दहलीज

  2. क्षैतिज और ऊर्ध्वाधर लाइनें निकालें। cv2.getStructuringElementतब क्षैतिज और ऊर्ध्वाधर गुठली का उपयोग करके रेखाएं बनाएंcv2.drawContours

  3. विकर्ण लाइनों, सर्कल ऑब्जेक्ट्स, और घुमावदार आकृति निकालें। गैर-पाठ आकृति को अलग करने के लिए समोच्च क्षेत्र cv2.contourArea और समोच्च सन्निकटन का उपयोग करके फ़िल्टर करेंcv2.approxPolyDP

  4. पाठ ROIs और OCR निकालें। फिर से खोजें और ROIs के लिए फ़िल्टर करें, फिर Pytesseract का उपयोग करके OCR ।


हरे रंग में प्रकाश डाला क्षैतिज रेखाओं को हटा दिया

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

खड़ी रेखाओं को हटा दिया

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

हटाए गए नॉन-टेक्स्ट कंट्रोस (विकर्ण रेखाएं, गोलाकार वस्तुएं और वक्र)

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

पाठ क्षेत्रों का पता लगाया

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

import cv2
import numpy as np
import pytesseract

pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"

# Load image, grayscale, Otsu's threshold
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
clean = thresh.copy()

# Remove horizontal lines
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15,1))
detect_horizontal = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
cnts = cv2.findContours(detect_horizontal, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

# Remove vertical lines
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,30))
detect_vertical = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, vertical_kernel, iterations=2)
cnts = cv2.findContours(detect_vertical, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(clean, [c], -1, 0, 3)

cnts = cv2.findContours(clean, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    # Remove diagonal lines
    area = cv2.contourArea(c)
    if area < 100:
        cv2.drawContours(clean, [c], -1, 0, 3)
    # Remove circle objects
    elif area > 1000:
        cv2.drawContours(clean, [c], -1, 0, -1)
    # Remove curve stuff
    peri = cv2.arcLength(c, True)
    approx = cv2.approxPolyDP(c, 0.02 * peri, True)
    x,y,w,h = cv2.boundingRect(c)
    if len(approx) == 4:
        cv2.rectangle(clean, (x, y), (x + w, y + h), 0, -1)

open_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,2))
opening = cv2.morphologyEx(clean, cv2.MORPH_OPEN, open_kernel, iterations=2)
close_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,2))
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, close_kernel, iterations=4)
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    x,y,w,h = cv2.boundingRect(c)
    area = cv2.contourArea(c)
    if area > 500:
        ROI = image[y:y+h, x:x+w]
        ROI = cv2.GaussianBlur(ROI, (3,3), 0)
        data = pytesseract.image_to_string(ROI, lang='eng',config='--psm 6')
        if data.isalnum():
            cv2.rectangle(image, (x, y), (x + w, y + h), (36,255,12), 2)
            print(data)

cv2.imwrite('image.png', image)
cv2.imwrite('clean.png', clean)
cv2.imwrite('close.png', close)
cv2.imwrite('opening.png', opening)
cv2.waitKey()

पहले उन लाइनों को हटाने के लिए अच्छा विचार है।
कार्लफिलिप

बुरे विचारों को भी अक्षरों को हटाने के लिए ...
वाल्टर ट्रॉस

8

ठीक है, यहाँ एक और संभव समाधान है। मुझे पता है कि आप पायथन के साथ काम करते हैं - मैं सी ++ के साथ काम करता हूं। मैं आपको कुछ विचार दूंगा और उम्मीद है, अगर आप ऐसा चाहते हैं, तो आप इस उत्तर को लागू करने में सक्षम होंगे।

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

मैं पूर्व-प्रसंस्करण का उपयोग नहीं करने की कोशिश कर रहा हूं क्योंकि: 1) फिल्टर और रूपात्मक चरण बूँद की गुणवत्ता को कम कर सकते हैं और 2) आपका लक्ष्य बूँदें कुछ विशेषताओं को प्रदर्शित करने के लिए प्रकट होती हैं जिनका हम शोषण कर सकते थे, मुख्यतः पहलू अनुपात और क्षेत्र

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

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

आइए एल्गोरिदम पर काम करते हैं। छवि को पढ़ने से शुरू करें और इसे आधे आयामों तक आकार दें। आपकी छवि वैसे ही बहुत बड़ी है। ग्रेस्केल में बदलें और ओत्सू के माध्यम से एक द्विआधारी छवि प्राप्त करें, यहाँ छद्म कोड में है:

//Read input:
inputImage = imread( "diagram.png" );

//Resize Image;
resizeScale = 0.5;

inputResized = imresize( inputImage, resizeScale );

//Convert to grayscale;
inputGray = rgb2gray( inputResized );

//Get binary image via otsu:
binaryImage = imbinarize( inputGray, "Otsu" );

ठंडा। हम इस छवि के साथ काम करेंगे। आपको हर सफेद बूँद की जांच करने की आवश्यकता है, और एक "गुण फ़िल्टर" लागू करें । मैं प्रत्येक घटक को लूप ट्रूप करने के लिए जुड़े हुए घटकों का उपयोग कर रहा हूं और अपने क्षेत्र और पहलू अनुपात को प्राप्त करता हूं , C ++ में यह निम्नानुसार किया जाता है:

//Prepare the output matrices:
cv::Mat outputLabels, stats, centroids;
int connectivity = 8;

//Run the binary image through connected components:
int numberofComponents = cv::connectedComponentsWithStats( binaryImage, outputLabels, stats, centroids, connectivity );

//Prepare a vector of colors  color the filtered blobs in black
std::vector<cv::Vec3b> colors(numberofComponents+1);
colors[0] = cv::Vec3b( 0, 0, 0 ); // Element 0 is the background, which remains black.

//loop through the detected blobs:
for( int i = 1; i <= numberofComponents; i++ ) {

    //get area:
    auto blobArea = stats.at<int>(i, cv::CC_STAT_AREA);

    //get height, width and compute aspect ratio:
    auto blobWidth = stats.at<int>(i, cv::CC_STAT_WIDTH);
    auto blobHeight = stats.at<int>(i, cv::CC_STAT_HEIGHT);
    float blobAspectRatio = (float)blobHeight/(float)blobWidth;

    //Filter your blobs

};

अब, हम गुण फ़िल्टर लागू करेंगे। यह पूर्व-गणना की गई थ्रेसहोल्ड के साथ तुलना है। मैंने निम्नलिखित मूल्यों का उपयोग किया:

Minimum Area: 40  Maximum Area:400
MinimumAspectRatio:  1

अपने forलूप के अंदर , इन मूल्यों के साथ वर्तमान बूँद गुणों की तुलना करें। यदि परीक्षण सकारात्मक हैं, तो आप काले रंग को "पेंट" करते हैं। forलूप के अंदर जारी है :

    //Filter your blobs

    //Test the current properties against the thresholds:
    bool areaTest =  (blobArea > maxArea)||(blobArea < minArea);
    bool aspectRatioTest = !(blobAspectRatio > minAspectRatio); //notice we are looking for TALL elements!

    //Paint the blob black:
    if( areaTest || aspectRatioTest ){
        //filtered blobs are colored in black:
        colors[i] = cv::Vec3b( 0, 0, 0 );
    }else{
        //unfiltered blobs are colored in white:
        colors[i] = cv::Vec3b( 255, 255, 255 );
    }

लूप के बाद, फ़िल्टर की गई छवि का निर्माण करें:

cv::Mat filteredMat = cv::Mat::zeros( binaryImage.size(), CV_8UC3 );
for( int y = 0; y < filteredMat.rows; y++ ){
    for( int x = 0; x < filteredMat.cols; x++ )
    {
        int label = outputLabels.at<int>(y, x);
        filteredMat.at<cv::Vec3b>(y, x) = colors[label];
    }
}

और बस यही सब है। आपने उन सभी तत्वों को फ़िल्टर किया, जो आप देख रहे हैं, जैसा नहीं है। एल्गोरिथ्म चलाने से आपको यह परिणाम मिलता है:

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

मैंने अतिरिक्त रूप से परिणामों की बेहतर कल्पना करने के लिए ब्लब्स के बाउंडिंग बॉक्स पाए हैं:

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

जैसा कि आप देखते हैं, कुछ तत्वों को याद किया जाता है। आप उन गुणों की बेहतर पहचान करने के लिए "गुण फ़िल्टर" को परिष्कृत कर सकते हैं जिन्हें आप देख रहे हैं। एक गहरा समाधान, जिसमें मशीन सीखने का थोड़ा सा समावेश होता है, को एक "आदर्श फीचर वेक्टर" के निर्माण की आवश्यकता होती है, जो कि बूँद से सुविधाओं को निकालता है, और दोनों वैक्टरों की एक समानता माप के माध्यम से तुलना करता है। परिणाम सुधारने के लिए आप कुछ पोस्ट -प्रोसेसर भी लगा सकते हैं ...

जो भी हो, आदमी, आपकी समस्या मामूली नहीं है और न ही आसान मापनीय है, और मैं आपको केवल विचार दे रहा हूं। उम्मीद है, आप अपने समाधान को लागू करने में सक्षम होंगे।


कोई भी मौका आप उसी कार्यक्रम को अजगर में बदल सकते हैं
पुलकित भटनागर

@PulkitBhatnagar हां, बिल्कुल। तुम बस कस के पकड़ लो, मेरे पास कुछ ही मिनटों में एक सही पोर्ट तैयार होगा।
eldesgraciado

?? क्या तुमने ऐसा किया, ताकि मैं तुम्हें इनाम पा
सकूं

आह येस। मुझे बहुत खेद है, मेरे साहब, मैं कुछ परेशानी में था, लेकिन रूपांतरण अच्छी तरह से हो रहा है। थोड़ा इंतज़ार करिये। धन्यवाद।
eldesgraciado

कभी नहीं सोचा कि व्यंग्य हो सकता है।
पुलकित भटनागर

4

एक विधि स्लाइडिंग विंडो का उपयोग करना है (यह महंगा है)।

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


मुझे लगता है कि 100bounty जवाब के लिए है
हिमांशु पोद्दार
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.