Dzielenie linii tekstu w zeskanowanym dokumencie
Próbuję znaleźć sposób na przełamanie podziału linii tekstu w zeskanowanym dokumencie, który został zaadaptowany. W tej chwili zapisuję wartości pikseli dokumentu jako niepodpisane inty od 0 do 255 i biorę średnią pikseli w każdej linii i dzielę linie na zakresy w zależności od tego, czy średnia wartości pikseli jest większa niż 250, a następnie biorę medianę każdego zakresu linii, dla których to dotyczy. Jednak metody te czasami zawodzą, ponieważ tam mogą być czarne plamy na obrazie.
Czy istnieje bardziej odporny na hałas sposób wykonania tego zadania?EDIT: oto jakiś kod. "warped" to nazwa oryginalnego obrazu, "cuts" to miejsce, w którym chcę podzielić obraz.
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
2 answers
Z obrazu wejściowego musisz zrobić tekst jako biały, a tło jako czarne
Następnie należy obliczyć kąt obrotu rachunku. Proste podejście polega na znalezieniu minAreaRect
wszystkich białych punktów (findNonZero
), a otrzymasz:
Następnie możesz obrócić rachunek, aby tekst był poziomy:
Teraz możesz obliczyć rzut poziomy (reduce
). Możesz wziąć średnią wartość w każda linia. Zastosuj próg th
na histogramie, aby uwzględnić jakiś szum na obrazie (tutaj użyłem 0
, tzn. brak szumu). Linie z tylko tłem będą miały wartość >0
, linie tekstowe będą miały wartość 0
w histogramie. Następnie weź średnią współrzędną kosza każdej ciągłej sekwencji białych koszy w histogramie. To będzie y
współrzędna Twoich linii:
Tutaj Kod. Jest w C++, ale ponieważ większość pracy jest z OpenCV funkcje, powinno być łatwo konwertowane do Pythona. Przynajmniej możesz użyć tego jako odniesienia:
#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;
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-24 10:12:55
Podstawowe kroki jako @Miki,
- przeczytaj Źródło
- threshed
- znajdź Minare
- warp by the rotated matrix
Znajdź i narysuj górną i dolną granicę]}
While kod w Pythonie :
#!/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")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
## (2) threshold
th, threshed = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV|cv2.THRESH_OTSU)
## (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)
Wynik końcowy :
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-11 09:49:22