Opencv kullanarak görüntüde bulunan tüm metinlerin konumunu öğrenin


11

İçinde metin (sayılar ve alfabe) içeren bu görüntü var. Bu resimde bulunan tüm metin ve sayıların yerini almak istiyorum. Ayrıca tüm metni de çıkarmak istiyorum.

resim açıklamasını buraya girin

Kordinatları ve resmimdeki tüm metni (sayılar ve alfabe) nasıl alabilirim. Örneğin 10B, 44, 16, 38, 22B vb.


tensorflow versiyonun nedir? Sürüm 2.1 ise, 2.0 yüklemeye çalışın
gellezzz

1
Ödülleri kötü sorulara atmak iyi bir uygulama değildir. Bunu nasıl yapacağınız hakkında hiçbir bilgi göstermediniz, bu yüzden geliştiricileri sadece birkaç tekrar karşılığında tam bir çözüm kodlamaya ikna etmeye çalışıyorsunuz. Bu nedenle mükemmel cevaplar görmeyi beklemiyorum, ancak zamanlarını ödemek için serbest web sitelerinde daha iyi çözümler alabileceğinizi düşünüyorum.
karlphillip

@karlphillip çok üzgünüm, ama ben yeni başlayan biriyim, başlamak için bir şeye ihtiyacım var değil mi? Bu konuda bana yardımcı olabilir misiniz
Pulkit Bhatnagar

Yanıtlar:


13

İşte metin dışı konturları filtrelemek için morfolojik işlemleri kullanan potansiyel bir yaklaşım . Fikir:

  1. İkili görüntü alın. Resim, gri tonlama ve ardından Otsu eşiğini yükle

  2. Yatay ve dikey çizgileri kaldırın. Kullanarak yatay ve dikey çekirdekler oluşturun ve cv2.getStructuringElementardındancv2.drawContours

  3. Çapraz çizgileri, daire nesnelerini ve kıvrımlı konturları kaldırın. Metin olmayan konturları izole etmek için kontur alanı cv2.contourArea ve kontur yaklaşımı kullanarak filtrelemecv2.approxPolyDP

  4. Metin YG'lerini ve OCR'yi ayıklayın. Konturları bulun ve Pytesseract kullanarak ROI'leri, sonra OCR'yi filtreleyin .


Yeşil renkle vurgulanmış yatay çizgiler kaldırıldı

resim açıklamasını buraya girin

Kaldırılan dikey çizgiler

resim açıklamasını buraya girin

Kaldırılan çeşitli metin dışı konturlar (çapraz çizgiler, dairesel nesneler ve eğriler)

resim açıklamasını buraya girin

Algılanan metin bölgeleri

resim açıklamasını buraya girin

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

Önce bu satırları kaldırmak iyi bir fikirdir.
karlphillip

Harf parçalarını kaldırmak için kötü fikir ...
Walter Tross

8

Pekala, işte başka bir olası çözüm. Python ile çalıştığını biliyorum - C ++ ile çalışıyorum. Size bazı fikirler vereceğim ve umarım, isterseniz, bu yanıtı uygulayabilirsiniz.

Ana fikir, ön işlemeyi hiç kullanmamak (en azından ilk aşamada değil) ve bunun yerine her hedef karaktere odaklanmak, bazı özellikler elde etmek ve her blobe bu özelliklere göre filtre uygulamaktır .

Ön işleme kullanmaya çalışmıyorum çünkü: 1) Filtreler ve morfolojik aşamalar lekelerin kalitesini düşürebilir ve 2) hedef lekeleriniz, özellikle en boy oranı ve alan gibi , kullanabileceğimiz bazı özellikler sergiler .

Şuna bir bak, sayılar ve harflerin hepsi daha geniş olduğundan daha uzun görünüyor ... ayrıca, belirli bir alan değeri içinde değişiyor gibi görünüyor. Örneğin, "çok geniş" veya "çok büyük" nesneleri atmak istersiniz .

Fikir şu ki, önceden hesaplanmış değerlere girmeyen her şeyi filtreleyeceğim. Karakterleri (sayılar ve harfler) inceledim ve minimum, maksimum alan değerleri ve minimum en boy oranı (burada yükseklik ve genişlik arasındaki oran) ile geldim.

Algoritma üzerinde çalışalım. Görüntüyü okuyarak ve boyutların yarısına göre yeniden boyutlandırarak başlayın. Resminiz çok büyük. Gri tonlamaya dönüştürün ve otsu aracılığıyla ikili bir görüntü elde edin, işte sözde kod:

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

Güzel. Bu görüntü ile çalışacağız. Her beyaz lekeyi incelemeniz ve bir "özellikler filtresi" uygulamanız gerekir . Her blob döngü döngü ve alan ve en boy oranı almak için istatistikleri ile bağlı bileşenleri kullanıyorum , C ++ bu aşağıdaki gibi yapılır:

//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

};

Şimdi özellikler filtresini uygulayacağız. Bu sadece önceden hesaplanan eşiklerle bir karşılaştırmadır. Aşağıdaki değerleri kullandım:

Minimum Area: 40  Maximum Area:400
MinimumAspectRatio:  1

Döngünüzün içinde for, geçerli blob özelliklerini bu değerlerle karşılaştırın. Testler pozitifse, damarı siyaha boyayabilirsiniz. forDöngünün içinde devam ediyor :

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

Döngüden sonra, filtrelenen görüntüyü oluşturun:

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];
    }
}

Ve… işte bu kadar. Aradığınız şeye benzemeyen tüm öğeleri filtrelediniz. Algoritmayı çalıştırdığınızda şu sonucu elde edersiniz:

resim açıklamasını buraya girin

Ayrıca sonuçları daha iyi görselleştirmek için lekelerin Sınırlayıcı Kutularını buldum:

resim açıklamasını buraya girin

Gördüğünüz gibi, bazı unsurlar gözden kaçar. Aradığınız karakterleri daha iyi tanımlamak için "özellikler filtresini" daraltabilirsiniz. Biraz makine öğrenimi içeren daha derin bir çözüm, "ideal özellik vektörü" nin oluşturulmasını, damlalardan özelliklerin çıkarılmasını ve her iki vektörü de benzerlik ölçüsü ile karşılaştırmayı gerektirir. Sonuçları iyileştirmek için bazı işlem sonrası işlemler de uygulayabilirsiniz ...

Her neyse, dostum, sorununuz önemsiz veya kolay ölçeklenebilir değil ve ben size sadece fikirler veriyorum. Umarım çözümünüzü uygulayabilirsiniz.


Herhangi bir şansı aynı programı
python'a

@PulkitBhatnagar Evet, elbette. Sadece sıkı durun, birkaç dakika içinde mükemmel bir limana hazır olacağım.
eldesgraciado

?? Sana ödül verebilmen için yaptın mı
Pulkit Bhatnagar

Ah evet. Çok üzgünüm, efendim, bir sorunla karşılaştım, ama dönüşüm güzel geliyor. Sadece bekle. Teşekkür.
eldesgraciado

bunun alaycı olabileceğini hiç düşünmemiştim.
Pulkit Bhatnagar

4

Bir yöntem sürgülü pencere kullanmaktır (Pahalıdır).

Görüntüdeki karakterlerin boyutunu belirleyin (tüm karakterler görüntüde göründüğü boyuttadır) ve pencerenin boyutunu ayarlayın. Algılama için tesseract yapmayı deneyin (Giriş görüntüsü ön işleme gerektirir). Bir pencere karakterleri art arda algılarsa, pencerenin koordinatlarını saklayın. Koordinatları birleştirin ve karakterlerin üzerindeki bölgeyi alın.


Sanırım 100bounty cevap için
Himanshu Poddar
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.