2016-01-24 9 views
16

Ich versuche einen Weg zu finden, die Aufteilung der Textzeilen in einem gescannten Dokument zu unterbrechen, das anpassungsfähig ist. Momentan speichere ich die Pixelwerte des Dokuments als vorzeichenlose Ints von 0 bis 255, und ich nehme den Durchschnitt der Pixel in jeder Zeile und teile die Zeilen in Bereiche auf, je nachdem, ob der Durchschnitt der Pixelwerte ist größer als 250, und dann nehme ich den Median von jedem Linienbereich, für den das gilt. Diese Methode schlägt jedoch manchmal fehl, da auf dem Bild schwarze Flecken auftreten können.Geteilte Textzeilen im gescannten Dokument

Gibt es eine lärmresistentere Möglichkeit, diese Aufgabe zu erledigen?

EDIT: Hier ist ein Code. "Warped" ist der Name des Originalbildes, "cuts" ist dort, wo ich das Bild teilen möchte.

warped = threshold_adaptive(warped, 250, offset = 10) 
warped = warped.astype("uint8") * 255 

# get areas where we can split image on whitespace to make OCR more accurate 
color_level = np.array([np.sum(line)/len(line) for line in warped]) 
cuts = [] 
i = 0 
while(i < len(color_level)): 
    if color_level[i] > 250: 
     begin = i 
     while(color_level[i] > 250): 
      i += 1 
     cuts.append((i + begin)/2) # middle of the whitespace region 
    else: 
     i += 1 

EDIT 2: Beispielbild hinzugefügt enter image description here

+0

Können Sie Code anzeigen? – RvdK

+0

Ok, ich habe den Code hinzugefügt, den ich benutze. – Alex

+0

ein Beispielbild wird hilfreich sein. – sturkmen

Antwort

25

Von Ihrem Eingangsbild, müssen Sie Text als weiße und Hintergrund als schwarz

enter image description here

Sie dann zu berechnen, müssen dafür der Drehwinkel Ihrer Rechnung. Ein einfacher Ansatz ist die minAreaRect alle weißen Punkte (findNonZero) zu finden, und Sie erhalten:

enter image description here

Dann können Sie Ihre Rechnung drehen, so dass der Text ist horizontal:

enter image description here

Jetzt können Sie die horizontale Projektion berechnen (reduce). Sie können den Durchschnittswert in jeder Zeile verwenden. Wende einen Schwellenwert th auf das Histogramm an, um etwas Rauschen in dem Bild zu berücksichtigen (hier verwendete ich 0, d. H. Kein Rauschen). Zeilen mit nur Hintergrund haben einen Wert >0, Textzeilen haben im Histogramm den Wert 0. Dann nehmen Sie die durchschnittliche Bin-Koordinate jeder kontinuierlichen Folge von weißen Bins im Histogramm. Das wird die y Ihrer Linien koordinieren:

enter image description here

Hier ist der Code. Es ist in C++, aber da die meiste Arbeit mit OpenCV-Funktionen ist, sollte es leicht in Python konvertierbar sein.Zumindest können Sie diese als Referenz verwenden:

#include <opencv2/opencv.hpp> 
using namespace cv; 
using namespace std; 

int main() 
{ 
    // Read image 
    Mat3b img = imread("path_to_image"); 

    // Binarize image. Text is white, background is black 
    Mat1b bin; 
    cvtColor(img, bin, COLOR_BGR2GRAY); 
    bin = bin < 200; 

    // Find all white pixels 
    vector<Point> pts; 
    findNonZero(bin, pts); 

    // Get rotated rect of white pixels 
    RotatedRect box = minAreaRect(pts); 
    if (box.size.width > box.size.height) 
    { 
     swap(box.size.width, box.size.height); 
     box.angle += 90.f; 
    } 

    Point2f vertices[4]; 
    box.points(vertices); 

    for (int i = 0; i < 4; ++i) 
    { 
     line(img, vertices[i], vertices[(i + 1) % 4], Scalar(0, 255, 0)); 
    } 

    // Rotate the image according to the found angle 
    Mat1b rotated; 
    Mat M = getRotationMatrix2D(box.center, box.angle, 1.0); 
    warpAffine(bin, rotated, M, bin.size()); 

    // Compute horizontal projections 
    Mat1f horProj; 
    reduce(rotated, horProj, 1, CV_REDUCE_AVG); 

    // Remove noise in histogram. White bins identify space lines, black bins identify text lines 
    float th = 0; 
    Mat1b hist = horProj <= th; 

    // Get mean coordinate of white white pixels groups 
    vector<int> ycoords; 
    int y = 0; 
    int count = 0; 
    bool isSpace = false; 
    for (int i = 0; i < rotated.rows; ++i) 
    { 
     if (!isSpace) 
     { 
      if (hist(i)) 
      { 
       isSpace = true; 
       count = 1; 
       y = i; 
      } 
     } 
     else 
     { 
      if (!hist(i)) 
      { 
       isSpace = false; 
       ycoords.push_back(y/count); 
      } 
      else 
      { 
       y += i; 
       count++; 
      } 
     } 
    } 

    // Draw line as final result 
    Mat3b result; 
    cvtColor(rotated, result, COLOR_GRAY2BGR); 
    for (int i = 0; i < ycoords.size(); ++i) 
    { 
     line(result, Point(0, ycoords[i]), Point(result.cols, ycoords[i]), Scalar(0, 255, 0)); 
    } 

    return 0; 
} 
+1

Super, vielen Dank !! – Alex

+1

Hallo Miki, in der Tat habe ich deine Antwort gesehen und zuvor abgestimmt. Ich habe es vergessen und habe den gleichen Ansatz verwendet, um eine Frage zu beantworten [answers.opencv.org/question/85884/](http://answers.opencv.org/question/85884/). Ich habe 'Canny' benutzt, um Trennlinien zu finden. vielleicht magst du es sehen. – sturkmen

+1

@sturkmen interessant, D – Miki

2

Grundlegende Schritte als @Miki,

  1. die Quelle lesen
  2. minAreaRect finden gedroschen
  3. warp durch die gedrehte Matrix
  4. finden und zeichnen upp ER- und untere Schranken

enter image description here


Während Code in Python:

#!/usr/bin/python3 
# 2018.01.16 01:11:49 CST 
# 2018.01.16 01:55:01 CST 
import cv2 
import numpy as np 

## (1) read 
img = cv2.imread("img02.jpg") 

## (2) threshold 
th, threshed = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU) 
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 

## (3) minAreaRect on the nozeros 
pts = cv2.findNonZero(threshed) 
ret = cv2.minAreaRect(pts) 

(cx,cy), (w,h), ang = ret 
if w>h: 
    w,h = h,w 
    ang += 90 

## (4) Find rotated matrix, do rotation 
M = cv2.getRotationMatrix2D((cx,cy), ang, 1.0) 
rotated = cv2.warpAffine(threshed, M, (img.shape[1], img.shape[0])) 

## (5) find and draw the upper and lower boundary of each lines 
hist = cv2.reduce(rotated,1, cv2.REDUCE_AVG).reshape(-1) 

th = 2 
H,W = img.shape[:2] 
uppers = [y for y in range(H-1) if hist[y]<=th and hist[y+1]>th] 
lowers = [y for y in range(H-1) if hist[y]>th and hist[y+1]<=th] 

rotated = cv2.cvtColor(rotated, cv2.COLOR_GRAY2BGR) 
for y in uppers: 
    cv2.line(rotated, (0,y), (W, y), (255,0,0), 1) 

for y in lowers: 
    cv2.line(rotated, (0,y), (W, y), (0,255,0), 1) 

cv2.imwrite("result.png", rotated) 

Schließlich ergeben:

enter image description here