Membersihkan gambar untuk OCR

9

Saya sudah mencoba menghapus gambar untuk OCR: (garis)

masukkan deskripsi gambar di sini

Saya perlu menghapus garis-garis ini untuk kadang-kadang lebih lanjut memproses gambar dan saya mendapatkan cukup dekat tetapi banyak waktu ambang terlalu banyak dari teks:

    copy = img.copy()
    blur = cv2.GaussianBlur(copy, (9,9), 0)
    thresh = cv2.adaptiveThreshold(blur,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV,11,30)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
    dilate = cv2.dilate(thresh, kernel, iterations=2)

    cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]

    for c in cnts:
        area = cv2.contourArea(c)
        if area > 300:
            x,y,w,h = cv2.boundingRect(c)
            cv2.rectangle(copy, (x, y), (x + w, y + h), (36,255,12), 3)

Sunting: Selain itu, menggunakan angka konstan tidak akan berfungsi jika font berubah. Apakah ada cara umum untuk melakukan ini?

K41F4r
sumber
2
Beberapa dari baris-baris ini, atau potongan-potongannya, memiliki karakteristik yang sama dengan teks hukum, dan akan sulit untuk menghilangkannya tanpa merusak teks yang valid. Jika ini berlaku, Anda mungkin fokus pada fakta bahwa mereka lebih panjang dari karakter, dan agak terisolasi. Jadi langkah pertama adalah memperkirakan ukuran dan kedekatan karakter.
Yves Daoust
@YvesDaoust Bagaimana cara menemukan kedekatan karakter? (karena memfilter murni berdasarkan ukuran sering kali dicampur dengan karakter)
K41F4r
1
Anda dapat menemukan, untuk setiap gumpalan, jarak ke tetangga terdekat. Kemudian dengan analisis histogram jarak, Anda akan menemukan ambang batas antara "tutup" dan "terpisah" (seperti mode distribusi), atau antara "dikelilingi" dan "terisolasi".
Yves Daoust
Jika beberapa garis kecil saling berdekatan, bukankah tetangga terdekatnya adalah garis kecil lainnya? Apakah menghitung jarak rata-rata ke semua gumpalan lainnya terlalu mahal?
K41F4r
"bukankah tetangga terdekat mereka akan menjadi garis kecil lainnya?": keberatan, Yang Mulia. Bahkan sekelompok segmen pendek dekat tidak berbeda dari teks yang sah, meskipun dalam pengaturan yang sama sekali tidak mungkin. Anda mungkin harus menyusun kembali fragmen garis yang putus. Saya tidak yakin bahwa jarak rata-rata untuk semua akan menyelamatkan Anda.
Yves Daoust

Jawaban:

14

Ini ide. Kami memecah masalah ini menjadi beberapa langkah:

  1. Tentukan rata-rata area kontur persegi panjang. Kami ambang lalu menemukan kontur dan filter menggunakan area persegi panjang pembatas dari kontur. Alasan kami melakukan ini adalah karena pengamatan bahwa setiap karakter khas hanya akan begitu besar sedangkan kebisingan besar akan menjangkau area persegi panjang yang lebih besar. Kami kemudian menentukan area rata-rata.

  2. Hapus kontur outlier besar. Kami beralih melalui kontur lagi dan menghapus kontur besar jika mereka 5xlebih besar dari area kontur rata-rata dengan mengisi kontur. Alih-alih menggunakan area ambang tetap, kami menggunakan ambang dinamis ini untuk ketahanan lebih.

  3. Dilatasi dengan kernel vertikal untuk menghubungkan karakter . Idenya adalah memanfaatkan pengamatan bahwa karakter disejajarkan dalam kolom. Dengan melebarkan dengan kernel vertikal kami menghubungkan teks bersama sehingga noise tidak akan dimasukkan dalam kontur gabungan ini.

  4. Hapus kebisingan kecil . Sekarang setelah teks disimpan, kami menemukan kontur dan menghapus kontur yang lebih kecil dari 4xarea kontur rata-rata.

  5. Bitwise-dan untuk merekonstruksi gambar . Karena kami hanya menginginkan kontur untuk tetap berada di topeng kami, kami bitwise-dan untuk melestarikan teks dan mendapatkan hasil kami.


Berikut visualisasi prosesnya:

Kami menetapkan ambang Otsu untuk mendapatkan gambar biner kemudian menemukan kontur untuk menentukan rata-rata area kontur persegi panjang. Dari sini kami menghapus kontur outlier besar yang disorot dengan warna hijau dengan mengisi kontur

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Selanjutnya kita membangun kernel vertikal dan melebar untuk menghubungkan karakter. Langkah ini menghubungkan semua teks yang diinginkan untuk menyimpan dan mengisolasi suara menjadi gumpalan individual.

masukkan deskripsi gambar di sini

Sekarang kami menemukan kontur dan filter menggunakan area kontur untuk menghilangkan kebisingan kecil

masukkan deskripsi gambar di sini

Berikut adalah semua partikel kebisingan yang dihapus yang disorot dengan warna hijau

masukkan deskripsi gambar di sini

Hasil

masukkan deskripsi gambar di sini

Kode

import cv2

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

# Determine average contour area
average_area = [] 
cnts = cv2.findContours(thresh, 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 = w * h
    average_area.append(area)

average = sum(average_area) / len(average_area)

# Remove large lines if contour area is 5x bigger then average contour area
cnts = cv2.findContours(thresh, 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 = w * h
    if area > average * 5:  
        cv2.drawContours(thresh, [c], -1, (0,0,0), -1)

# Dilate with vertical kernel to connect characters
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2,5))
dilate = cv2.dilate(thresh, kernel, iterations=3)

# Remove small noise if contour area is smaller than 4x average
cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    area = cv2.contourArea(c)
    if area < average * 4:
        cv2.drawContours(dilate, [c], -1, (0,0,0), -1)

# Bitwise mask with input image
result = cv2.bitwise_and(image, image, mask=dilate)
result[dilate==0] = (255,255,255)

cv2.imshow('result', result)
cv2.imshow('dilate', dilate)
cv2.imshow('thresh', thresh)
cv2.waitKey()

Catatan: Pemrosesan gambar tradisional terbatas pada ambang, operasi morfologis, dan pemfilteran kontur (perkiraan kontur, area, rasio aspek, atau deteksi gumpalan). Karena gambar input dapat bervariasi berdasarkan ukuran teks karakter, menemukan solusi tunggal cukup sulit. Anda mungkin ingin melihat pelatihan classifier Anda sendiri dengan pembelajaran mesin / mendalam untuk solusi yang dinamis.

nathancy
sumber
1
Dalam hal font yang lebih besar, bukankah ini juga akan menghapus teks?
K41F4r
Ya mungkin, jadi Anda harus menyesuaikan nilai area ambang. Untuk pendekatan yang lebih dinamis, sebuah ide adalah menentukan area karakter rata-rata kemudian menggunakannya sebagai ambang
nathancy
Tampaknya terlalu spesifik untuk contoh, menggunakan area rata-rata masih akan banyak menghapus teks yang memperburuk hasil untuk OCR
K41F4r
Apakah Anda memiliki contoh lain input gambar yang dapat Anda tambahkan ke posting?
nathancy
1
Menemukan solusi yang berfungsi dalam semua situasi menggunakan teknik pemrosesan gambar tradisional cukup sulit. Anda mungkin ingin melihat pelatihan classifier Anda sendiri menggunakan pembelajaran yang mendalam. Semoga berhasil!
nathancy