Heim > Backend-Entwicklung > Python-Tutorial > OpenCV: Spalten in arabischen Zeitschriften finden (Python)

OpenCV: Spalten in arabischen Zeitschriften finden (Python)

WBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWBOYWB
Freigeben: 2024-02-22 12:52:11
nach vorne
701 Leute haben es durchsucht

OpenCV: Spalten in arabischen Zeitschriften finden (Python)

Frageninhalt

Ich bin neu bei opencv und neu bei Python. Ich habe versucht, Code, den ich online gefunden habe, zusammenzusetzen, um mein Forschungsproblem zu lösen. Ich habe ein arabisches Tagebuch aus dem Jahr 1870, das Hunderte von Seiten hat, jede Seite enthält zwei Spalten und hat einen dicken schwarzen Rand. Ich möchte zwei Spalten als Bilddateien extrahieren, damit ich sie einzeln mit OCR bearbeiten und dabei die Kopf- und Fußzeile ignorieren kann. Hier ist eine Beispielseite:

Seite 3

Ich habe zehn Seiten des Originaldrucks als separate PNG-Dateien. Ich habe das folgende Skript geschrieben, um jeden einzelnen zu behandeln. Auf zwei der zehn Seiten funktioniert es wie erwartet, auf den anderen acht Seiten gelingt es jedoch nicht, die Spalten zu generieren. Ich verstehe nicht alle Funktionen gut genug, um zu wissen, wo ich diese Werte verwenden könnte, oder wenn mein gesamter Ansatz falsch ist, denke ich, dass der beste Weg, dies zu lernen, darin besteht, die Community zu fragen, wie Sie dieses Problem lösen würden.

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")
Nach dem Login kopieren

Im Anschluss an das Tutorial habe ich eine unscharfe und erweiterte binäre Umkehrung erstellt, damit die verschiedenen rechteckigen Bereiche anhand der großen weißen Fläche identifiziert werden können. Ich habe auch eine Kopie jeder erweiterten Version gespeichert, damit ich sehen kann, wie sie aussieht. Hier ist die Seite oben nach der Verarbeitung:

Seite 3 wurde vergrößert

Die „for c in cnts“-Schleife sollte große rechteckige Bereiche im Bild finden. Wenn das Seitenverhältnis kleiner als 2,5 ist, erhalte ich eine ganze Seite (ohne Kopf- und Fußzeile, was gut funktioniert). Wenn das Seitenverhältnis größer ist, weiß ich, dass es sich um eine Spalte handelt, und es speichert diese z. B. temp/p2-col2.png

Ich habe ein paar schöne ganze Seiten ohne Kopf- und Fußzeilen bekommen, also nur mit großen schwarzen Rändern, aber nicht in Spalten zerstückelt. Auf 2 von 10 Seiten habe ich bekommen, was ich wollte, nämlich:

Erfolgskolumne auf Seite 2

Da ich manchmal die gewünschten Ergebnisse erhalte, muss irgendetwas funktionieren, aber ich weiß nicht, wie ich es weiter verbessern kann.

Herausgeber:

Hier sind weitere Seitenbeispiele:

p0

p1

p5


Richtige Antwort


Ich habe etwas ohne Erweiterung ausprobiert, weil ich sehen wollte, ob ich die Mittellinie einfach als „Trennzeichen“ verwenden kann. Hier ist der Code:

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
Nach dem Login kopieren

Ich mache keine Aussagen zum Seitenverhältnis, also ist das vielleicht noch etwas, was Sie tun müssen..

Grundsätzlich sind die wichtigsten Linien bei dieser Methode die Erzeugung linker und rechter Konturen basierend auf x-Koordinaten. Dies ist das Endergebnis, das ich erhalten habe:

Es gibt immer noch einige schwarze Stellen an den Rändern, aber das sollte für OCR kein Problem darstellen.

Zu Ihrer Information: Ich verwende die folgenden Pakete in Jupyter:

import cv2
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
Nach dem Login kopieren

v2.0: Implementiert nur mit der Erkennung großer Kästchen:

Also habe ich etwas geweitet und die große Box war leicht zu erkennen. Ich verwende einen horizontalen Kernel, um sicherzustellen, dass die vertikalen Linien der großen Box immer dick genug sind, um erkannt zu werden. Allerdings kann ich das Problem mit der Mittellinie nicht lösen, da diese sehr dünn ist... Nichtsdestotrotz ist hier der Code für die obige Methode:

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")
Nach dem Login kopieren

Das sind die Ergebnisse. Sie können feststellen, dass es nicht perfekt ist, aber da Ihr Ziel OCR ist, sollte dies kein Problem sein.

Bitte sagen Sie mir, ob das funktioniert. Wenn nicht, werde ich mir den Kopf zerbrechen, um eine bessere Lösung zu finden...

v3.0: Eine bessere Möglichkeit, geradere Bilder zu erhalten, was die Qualität der OCR verbessert.

Inspiriert von meiner anderen Antwort hier: Antwort. Es ist sinnvoll, das Bild zu begradigen, damit die OCR bessere Ergebnisse liefert. Daher habe ich eine Vierpunkttransformation für den erkannten Außenrahmen verwendet. Dadurch wird das Bild etwas begradigt und der Text horizontaler. Hier ist der Code:

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")
Nach dem Login kopieren

Hat die folgenden Pakete:

import cv2
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
from imutils.perspective import four_point_transform
Nach dem Login kopieren

Wie Sie dem Code entnehmen können, ist dies ein besserer Ansatz. Dank der Vierpunkttransformation können Sie erzwingen, dass das Bild zentriert und horizontal ist. Darüber hinaus besteht keine Notwendigkeit für Überlappungen, da die Bilder gut voneinander getrennt sind. Hier ist ein Beispiel als Referenz:

Das obige ist der detaillierte Inhalt vonOpenCV: Spalten in arabischen Zeitschriften finden (Python). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:stackoverflow.com
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage