Dapatkan lokasi semua teks yang ada dalam gambar menggunakan opencv

11

Saya memiliki gambar ini yang berisi teks (angka dan huruf) di dalamnya. Saya ingin mendapatkan lokasi dari semua teks dan angka yang ada di gambar ini. Saya juga ingin mengekstraksi semua teks.

masukkan deskripsi gambar di sini

Bagaimana cara mendapatkan kordinat serta semua teks (angka dan huruf) di gambar saya. Untuk mis 10B, 44, 16, 38, 22B dll

Pulkit Bhatnagar
sumber
apa versi tensorflow Anda? Jika versi Anda 2.1, coba instal 2.0
gellezzz
1
Melempar karunia pada pertanyaan buruk itu bukan praktik yang baik. Anda tidak menunjukkan pengetahuan tentang cara melakukan ini sehingga sepertinya Anda hanya mencoba untuk memikat pengembang agar mengkode solusi lengkap dengan imbalan beberapa poin rep. Saya tidak berharap melihat jawaban sempurna karena alasan itu, tetapi saya yakin Anda bisa mendapatkan solusi yang lebih baik di situs web lepas jika Anda membayar orang untuk waktu mereka.
karlphillip
@karlphillip sangat menyesal, tetapi saya seorang pemula saya perlu sesuatu untuk memulai, bukan? Bisakah Anda membantu saya dengan yang ini
Pulkit Bhatnagar

Jawaban:

13

Berikut adalah pendekatan potensial menggunakan operasi morfologis untuk memfilter kontur non-teks. Idenya adalah:

  1. Dapatkan gambar biner. Memuat gambar, skala abu-abu, lalu ambang batas Otsu

  2. Hapus garis horizontal dan vertikal. Buat kernel horisontal dan vertikal menggunakan cv2.getStructuringElementlalu hapus garis dengancv2.drawContours

  3. Hapus garis diagonal, objek lingkaran, dan kontur melengkung. Saring menggunakan area kontur cv2.contourArea dan pendekatan kontur cv2.approxPolyDP untuk mengisolasi kontur non-teks

  4. Ekstrak ROI dan OCR teks. Temukan kontur dan filter untuk ROI kemudian OCR menggunakan Pytesseract .


Garis horizontal yang dihapus disorot dalam warna hijau

masukkan deskripsi gambar di sini

Garis vertikal yang dihapus

masukkan deskripsi gambar di sini

Menghapus berbagai kontur non-teks (garis diagonal, objek melingkar, dan kurva)

masukkan deskripsi gambar di sini

Wilayah teks yang terdeteksi

masukkan deskripsi gambar di sini

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()
nathancy
sumber
Ide bagus untuk menghapus garis itu terlebih dahulu.
karlphillip
ide buruk untuk juga menghapus bagian surat ...
Walter Tross
8

Baiklah, inilah solusi lain yang mungkin. Saya tahu Anda bekerja dengan Python - Saya bekerja dengan C ++. Saya akan memberi Anda beberapa ide dan mudah-mudahan, jika Anda menginginkannya, Anda akan dapat menerapkan jawaban ini.

Gagasan utamanya adalah untuk tidak menggunakan pra-pemrosesan sama sekali (setidaknya tidak pada tahap awal) dan sebaliknya fokus pada setiap karakter target, dapatkan beberapa properti , dan filter setiap gumpalan sesuai dengan properti ini.

Saya mencoba untuk tidak menggunakan pra-pemrosesan karena: 1) Filter dan tahapan morfologis dapat menurunkan kualitas gumpalan dan 2) gumpalan target Anda tampaknya menunjukkan beberapa karakteristik yang dapat kami eksploitasi, terutama: rasio aspek dan area .

Lihat saja, angka dan huruf semuanya tampak lebih tinggi daripada yang lebih luas ... lebih jauh lagi, semuanya tampak bervariasi dalam nilai area tertentu. Misalnya, Anda ingin membuang objek "terlalu lebar" atau "terlalu besar" .

Idenya adalah bahwa saya akan memfilter semua yang tidak termasuk dalam nilai yang telah dihitung sebelumnya. Saya memeriksa karakter (angka dan huruf) dan datang dengan nilai area minimum, maksimum dan rasio aspek minimum (di sini, rasio antara tinggi dan lebar).

Mari kita bekerja pada algoritma. Mulailah dengan membaca gambar dan mengubah ukurannya menjadi setengah dimensi. Gambar Anda terlalu besar. Konversikan ke skala abu-abu dan dapatkan gambar biner melalui otsu, berikut dalam pseudo-code:

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

Keren. Kami akan bekerja dengan gambar ini. Anda perlu memeriksa setiap gumpalan putih, dan menerapkan "filter properti" . Saya menggunakan komponen yang terhubung dengan statistik untuk loop melalui setiap gumpalan dan mendapatkan area dan aspek rasio, di C ++ ini dilakukan sebagai berikut:

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

};

Sekarang, kita akan menerapkan filter properti. Ini hanya perbandingan dengan ambang yang dihitung sebelumnya. Saya menggunakan nilai-nilai berikut:

Minimum Area: 40  Maximum Area:400
MinimumAspectRatio:  1

Di dalam forlingkaran Anda , bandingkan properti gumpalan saat ini dengan nilai-nilai ini. Jika hasil tes positif, Anda "mengecat" gumpalan hitam. Melanjutkan di dalam forlingkaran:

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

Setelah loop, buat gambar yang difilter:

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

Dan ... cukup banyak. Anda memfilter semua elemen yang tidak mirip dengan yang Anda cari. Menjalankan algoritme Anda mendapatkan hasil ini:

masukkan deskripsi gambar di sini

Saya juga telah menemukan Bounding Boxes of the blob untuk lebih memvisualisasikan hasil:

masukkan deskripsi gambar di sini

Seperti yang Anda lihat, beberapa elemen tidak terdeteksi. Anda dapat memperbaiki "filter properti" untuk mengidentifikasi karakter yang Anda cari dengan lebih baik. Solusi yang lebih dalam, yang melibatkan sedikit pembelajaran mesin, membutuhkan konstruksi "vektor fitur ideal", mengekstraksi fitur dari gumpalan, dan membandingkan kedua vektor melalui ukuran kesamaan. Anda juga dapat menerapkan beberapa proses pasca untuk meningkatkan hasil ...

Apa pun, kawan, masalah Anda tidak sepele atau tidak mudah diukur, dan saya hanya memberi Anda ide. Semoga Anda dapat menerapkan solusi Anda.

eldesgraciado
sumber
Kesempatan Anda dapat mengonversi program yang sama ke python
Pulkit Bhatnagar
@ PulkitBhatnagar Ya, tentu saja. Anda hanya memegang erat-erat, saya akan memiliki port yang sempurna siap dalam beberapa menit.
eldesgraciado
?? apakah Anda melakukannya, sehingga saya dapat memberikan hadiah kepada Anda
Pulkit Bhatnagar
Ah iya. Saya benar-benar minta maaf, tuan saya, saya mengalami beberapa masalah, tetapi pertobatan datang dengan baik. Tunggu saja. Terima kasih.
eldesgraciado
tidak pernah bertanya-tanya itu bisa menjadi sarkasme.
Pulkit Bhatnagar
4

Salah satu metode adalah dengan menggunakan jendela geser (Itu mahal).

Tentukan ukuran karakter dalam gambar (semua karakter memiliki ukuran yang sama seperti yang terlihat pada gambar) dan atur ukuran jendela. Coba tesseract untuk deteksi (Gambar input membutuhkan pra pemrosesan). Jika sebuah jendela mendeteksi karakter secara berurutan, maka simpan koordinat jendela tersebut. Gabungkan koordinat dan dapatkan wilayah pada karakter.

saveearth
sumber
Saya pikir 100bounty adalah jawabannya
Himanshu Poddar