Rumah  >  Artikel  >  pembangunan bahagian belakang  >  OpenCV: Cari lajur dalam jurnal Arab (Python)

OpenCV: Cari lajur dalam jurnal Arab (Python)

WBOY
WBOYke hadapan
2024-02-22 12:52:11628semak imbas

OpenCV: Cari lajur dalam jurnal Arab (Python)

Kandungan soalan

Saya baru kenal opencv dan baru kenal python. Saya cuba menyusun kod yang saya temui dalam talian untuk menyelesaikan masalah penyelidikan saya. Saya mempunyai diari Arab tahun 1870 yang mempunyai ratusan halaman, setiap halaman mengandungi dua lajur dan mempunyai sempadan hitam tebal. Saya ingin mengekstrak dua lajur sebagai fail imej supaya saya boleh menjalankan ocr pada mereka secara individu sambil mengabaikan pengepala dan pengaki. Berikut ialah halaman contoh:

Halaman 3

Saya mempunyai sepuluh muka surat cetakan asal sebagai fail png yang berasingan. Saya menulis skrip berikut untuk mengendalikan setiap satu. Ia berfungsi seperti yang dijangkakan dalam 2 daripada 10 halaman, tetapi gagal menjana lajur dalam 8 halaman yang lain. Saya tidak memahami semua fungsi dengan cukup baik untuk mengetahui di mana saya boleh menggunakan nilai ini, atau jika keseluruhan pendekatan saya tersasar - Saya fikir cara terbaik untuk belajar ialah bertanya kepada komuniti bagaimana anda akan menyelesaikan masalah ini.

import cv2

def cutpage(fname, pnum):
    image = cv2.imread(fname)
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (7,7), 0)
    thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 13))
    dilate = cv2.dilate(thresh, kernel, iterations=1)
    dilatename = "temp/dilate" + str(pnum) + ".png"
    cv2.imwrite(dilatename, dilate)
    cnts = cv2.findContours(dilate, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    cnts = sorted(cnts, key=lambda x: cv2.boundingRect(x)[0])

    fullpage=1
    column=1
    for c in cnts:
        x, y, w, h = cv2.boundingRect(c)
        if h > 300 and w > 20:
            if (h/w)<2.5:
                print("Found full page: ", x, y, w, h)
                filename = "temp/p" + str(pnum) + "-full" + str(fullpage) + ".png"
                fullpage+=1
            else:
                print("Found column: ", x, y, w, h)
                filename = "temp/p" + str(pnum) + "-col" + str(column) + ".png"
                column+=1
            roi = image[y:y+h, x:x+w]
            cv2.imwrite(filename, roi)
    return (column-1)
        
for nr in range(10):
    filename = "p"+str(nr)+".png"
    print("Checking page", nr)
    diditwork = cutpage(filename, nr)
    print("Found", diditwork, "columns")

Berikutan tutorial, saya mencipta penyongsangan binari yang kabur dan diluaskan supaya ia dapat mengenal pasti kawasan segi empat tepat yang berbeza oleh kawasan putih yang besar. Saya juga menyimpan salinan setiap versi lanjutan supaya saya dapat melihat rupanya, berikut ialah halaman di atas selepas diproses:

Muka surat 3 telah dibesarkan

Gelung "untuk c dalam cnts" harus mencari kawasan segi empat tepat yang besar dalam imej. Jika nisbah aspek kurang daripada 2.5 saya mendapat halaman penuh (tanpa pengepala dan pengaki, yang berfungsi dengan baik), jika nisbah aspek lebih besar daripada ini, saya tahu ia adalah lajur dan ia menyimpannya cth. temp/ p2-col2.png

Saya mendapat beberapa halaman penuh yang bagus tanpa pengepala dan pengaki, iaitu, hanya sempadan hitam yang besar, tetapi tidak dicincang menjadi lajur. Dalam 2 muka surat daripada 10 saya mendapat apa yang saya mahu, iaitu:

Lajur Kejayaan di Halaman 2

Memandangkan saya kadang-kadang mendapat hasil yang diinginkan, mesti ada sesuatu yang berkesan, tetapi saya tidak tahu bagaimana untuk memperbaikinya lagi.

Editor:

Berikut adalah lebih banyak contoh halaman:

p0

p1

p5


Jawapan betul


Saya mencuba sesuatu tanpa sebarang pengembangan kerana saya ingin melihat sama ada saya boleh menggunakan garis tengah sebagai "pemisah". Ini kodnya:

im = cv2.cvtcolor(cv2.imread("arabic.png"), cv2.color_bgr2rgb) # read im as rgb for better plots
gray = cv2.cvtcolor(im, cv2.color_rgb2gray) # convert to gray
_, threshold = cv2.threshold(gray, 250, 255, cv2.thresh_binary_inv) # inverse thresholding
contours, _ = cv2.findcontours(threshold, cv2.retr_external, cv2.chain_approx_none) # find contours
sortedcontours = sorted(contours, key = cv2.contourarea, reverse=true) # sort according to area, descending
bigbox = sortedcontours[0] # get the contour of the big box
middleline = sortedcontours[1] # get the contour of the vertical line
xmiddleline, _, _, _ = cv2.boundingrect(middleline) # get x coordinate of middleline
leftboxcontour = np.array([point for point in bigbox if point[0, 0] < xmiddleline]) # assign left of line as points from the big contour
rightboxcontour = np.array([point for point in bigbox if point[0, 0] >= xmiddleline]) # assigh right of line as points from the big contour
leftboxx, leftboxy, leftboxw, leftboxh = cv2.boundingrect(leftboxcontour) # get properties of box on left
rightboxx, rightboxy, rightboxw, rightboxh = cv2.boundingrect(rightboxcontour) # get properties of box on right
leftboxcrop = im[leftboxy:leftboxy + leftboxh, leftboxx:leftboxx + leftboxw] # crop left 
rightboxcrop = im[rightboxy:rightboxy + rightboxh, rightboxx:rightboxx + rightboxw] # crop right
# maybe do you assertations about aspect ratio??
cv2.imwrite("right.png", rightboxcrop) # save image
cv2.imwrite("left.png", leftboxcrop) # save image

Saya tidak menggunakan sebarang penegasan tentang nisbah aspek, jadi mungkin ini masih perlu anda lakukan..

Pada asasnya, garisan yang paling penting dalam kaedah ini adalah menghasilkan kontur kiri dan kanan berdasarkan koordinat x. Ini adalah keputusan akhir yang saya dapat:

Masih ada sedikit bahagian hitam di bahagian tepi, tetapi itu tidak menjadi masalah untuk ocr.

FYI: Saya menggunakan pakej berikut dalam jupyter:

import cv2
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt

v2.0: Dilaksanakan hanya menggunakan pengesanan kotak besar:

Jadi saya melakukan sedikit pelebaran dan kotak besar itu mudah dikesan. Saya menggunakan kernel mendatar untuk memastikan bahawa garis menegak kotak besar sentiasa cukup tebal untuk dikesan. Walau bagaimanapun, saya tidak dapat menyelesaikan masalah dengan garis tengah kerana ia sangat nipis... Namun begitu, berikut adalah kod untuk kaedah di atas:

im = cv2.cvtcolor(cv2.imread("1.png"), cv2.color_bgr2rgb) # read im as rgb for better plots
gray = cv2.cvtcolor(im, cv2.color_rgb2gray) # convert to gray
gray[gray<255] = 0 # added some contrast to make it either completly black or white
_, threshold = cv2.threshold(gray, 250, 255, cv2.thresh_binary_inv) # inverse thresholding
thresholddilated = cv2.dilate(threshold, np.ones((1,10)), iterations = 1) # dilate horizontally
contours, _ = cv2.findcontours(thresholddilated, cv2.retr_external, cv2.chain_approx_none) # find contours
sortedcontours = sorted(contours, key = cv2.contourarea, reverse=true) # sort according to area, descending
x, y, w, h = cv2.boundingrect(sortedcontours[0]) # get the bounding rect properties of the contour
left = im[y:y+h, x:x+int(w/2)+10].copy() # generate left, i included 10 pix from the right just in case
right = im[y:y+h, int(w/2)-10:w].copy() # and right, i included 10 pix from the left just in case
fig, ax = plt.subplots(nrows = 2, ncols = 3) # plotting...
ax[0,0].axis("off")
ax[0,1].imshow(im)
ax[0,1].axis("off")
ax[0,2].axis("off")
ax[1,0].imshow(left)
ax[1,0].axis("off")
ax[1,1].axis("off")
ax[1,2].imshow(right)
ax[1,2].axis("off")

Ini adalah hasilnya, anda boleh melihat ia tidak sempurna, tetapi sekali lagi, kerana sasaran anda adalah ocr, ini tidak sepatutnya menjadi masalah.

Tolong beritahu saya jika ini berkesan, jika tidak saya akan memerah otak saya untuk mencari penyelesaian yang lebih baik...

v3.0: Cara yang lebih baik untuk mendapatkan imej yang lebih lurus, yang akan meningkatkan kualiti ocr.

Diinspirasikan oleh jawapan saya yang lain di sini: jawab. Adalah wajar untuk meluruskan imej supaya ocr mempunyai hasil yang lebih baik. Oleh itu, saya menggunakan transformasi empat mata pada bingkai luar yang dikesan. Ini akan meluruskan sedikit imej dan menjadikan teks lebih mendatar. Ini kodnya:

im = cv2.cvtcolor(cv2.imread("2.png"), cv2.color_bgr2rgb) # read im as rgb for better plots
gray = cv2.cvtcolor(im, cv2.color_rgb2gray) # convert to gray
gray[gray<255] = 0 # added some contrast to make it either completly black or white
_, threshold = cv2.threshold(gray, 250, 255, cv2.thresh_binary_inv) # inverse thresholding
thresholddilated = cv2.dilate(threshold, np.ones((1,10)), iterations = 1) # dilate horizontally
contours, _ = cv2.findcontours(thresholddilated, cv2.retr_external, cv2.chain_approx_none) # find contours
largest_contour = max(contours, key = cv2.contourarea) # get largest contour
hull = cv2.convexhull(largest_contour) # get the hull
epsilon = 0.02 * cv2.arclength(largest_contour, true) # epsilon
pts1 = np.float32(cv2.approxpolydp(hull, epsilon, true).reshape(-1, 2)) # get the points
result = four_point_transform(im, pts1) # using imutils
height, width = result.shape[:2] # get the dimensions of the transformed image
left = result[:, 0:int(width/2)].copy() # from the beginning to half the width
right = result[:, int(width/2): width].copy() # from half the width till the end
fig, ax = plt.subplots(nrows = 2, ncols = 3) # plotting...
ax[0,0].axis("off")
ax[0,1].imshow(result)
ax[0,1].axvline(width/2)
ax[0,1].axis("off")
ax[0,2].axis("off")
ax[1,0].imshow(left)
ax[1,0].axis("off")
ax[1,1].axis("off")
ax[1,2].imshow(right)
ax[1,2].axis("off")

Mempunyai pakej berikut:

import cv2
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
from imutils.perspective import four_point_transform

Seperti yang anda boleh lihat daripada kod, ini adalah pendekatan yang lebih baik, anda boleh memaksa imej untuk dipusatkan dan mendatar terima kasih kepada transformasi empat titik. Tambahan pula, tidak perlu memasukkan beberapa pertindihan kerana imej dipisahkan dengan baik. Berikut adalah contoh untuk rujukan anda:

Atas ialah kandungan terperinci OpenCV: Cari lajur dalam jurnal Arab (Python). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:stackoverflow.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam