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