image processing - source - Procesamiento de imágenes para mejorar la precisión de tesseract OCR
tesseract ocr python opencv (10)
- corregir DPI (si es necesario) 300 DPI es mínimo
- corregir el tamaño del texto (por ejemplo, 12 puntos deberían estar bien)
- intenta arreglar las líneas de texto (texto de alineación y corrección)
- Intente corregir la iluminación de la imagen (p. ej., ninguna parte oscura de la imagen)
- imagen binarize y de-noise
No existe una línea de comando universal que se ajuste a todos los casos (a veces es necesario desenfocar y enfocar la imagen). Pero puedes probar TEXTCLEANER desde los scripts de ImageMagick de Fred .
Si no eres seguidor de la línea de comando, tal vez puedas intentar usar opensource scantailor.sourceforge.net o bookrestorer comercial.
He estado usando tesseract para convertir documentos en texto. La calidad de los documentos varía enormemente, y estoy buscando consejos sobre qué tipo de procesamiento de imágenes podría mejorar los resultados. Me he dado cuenta de que el texto altamente pixelado (por ejemplo, el generado por las máquinas de fax) es especialmente difícil de procesar para Tesseract; presumiblemente, todos los bordes dentados de los caracteres confunden los algoritmos de reconocimiento de forma.
¿Qué tipo de técnicas de procesamiento de imágenes mejorarían la precisión? He estado usando un desenfoque Gaussiano para suavizar las imágenes pixeladas y he visto algunas pequeñas mejoras, pero espero que haya una técnica más específica que produzca mejores resultados. Digamos un filtro sintonizado en imágenes en blanco y negro, que suavizaría los bordes irregulares, seguido de un filtro que aumentaría el contraste para hacer que los personajes sean más distintos.
¿Algún consejo general para alguien que es un novato en el procesamiento de imágenes?
De ninguna manera soy un experto en OCR. Pero esta semana he tenido que convertir el texto de un jpg.
Empecé con un jpg en color RGB de 445x747 píxeles. Intenté tesseract de inmediato con esto, y el programa no convirtió casi nada. Luego entré en GIMP e hice lo siguiente. imagen> modo> imagen en escala de grises> imagen en escala> filtros de 1191x2000 píxeles> mejorar> máscara de enfoque con valores de radio = 6.8, cantidad = 2.69, umbral = 0 Luego guardé como un nuevo jpg al 100% de calidad.
Tesseract luego fue capaz de extraer todo el texto en un archivo .txt
Gimp es tu amigo.
El umbral de adaptación es importante si la iluminación es desigual en toda la imagen. Mi preprocesamiento con GraphicsMagic se menciona en esta publicación: https://groups.google.com/forum/#!topic/tesseract-ocr/jONGSChLRv4
GraphicsMagic también tiene la función -lat para el umbral adaptable de tiempo lineal que intentaré pronto.
Otro método de umbralización usando OpenCV se describe aquí: http://docs.opencv.org/trunk/doc/py_tutorials/py_imgproc/py_thresholding/py_thresholding.html
Esto es hace un tiempo, pero aún podría ser útil.
Mi experiencia muestra que a veces ayuda a cambiar el tamaño de la imagen en la memoria antes de pasarla a tesseract.
Pruebe diferentes modos de interpolación. La publicación https://.com/a/4756906/146003 me ayudó mucho.
Hice esto para obtener buenos resultados de una imagen que no tiene texto muy pequeño.
- Aplica desenfoque a la imagen original.
- Aplicar umbral adaptativo.
- Aplicar efecto de nitidez.
Y si todavía no se obtienen buenos resultados, escale la imagen al 150% o al 200%.
La documentación de Tesseract contiene algunos buenos detalles sobre cómo mejorar la calidad del OCR a través de los pasos de procesamiento de imágenes.
Hasta cierto punto, Tesseract los aplica automáticamente. También es posible decirle a Tesseract que escriba una imagen intermedia para su inspección, es decir, para verificar qué tan bien funciona el procesamiento interno de imágenes (busque tessedit_write_images
en la referencia anterior).
Más importante aún, el nuevo sistema de red neuronal en Tesseract 4 produce resultados de OCR mucho mejores, en general y especialmente para imágenes con algo de ruido. Está habilitado con --oem 1
, por ejemplo como en:
$ tesseract --oem 1 -l deu page.png result pdf
(este ejemplo selecciona el idioma alemán)
Por lo tanto, tiene sentido probar primero qué tan lejos se llega con el nuevo modo Tesseract LSTM antes de aplicar algunos pasos de procesamiento de imagen de preprocesamiento personalizado.
(a fines de 2017, Tesseract 4 aún no se ha publicado como estable, pero la versión de desarrollo es utilizable)
Leer texto de documentos de imagen usando cualquier motor de OCR tiene muchos problemas para obtener una buena precisión. No hay una solución fija para todos los casos, pero aquí hay algunas cosas que se deben considerar para mejorar los resultados de OCR.
1) Presencia de ruido debido a una mala calidad de imagen / elementos / blobs no deseados en la región de fondo. Esto requiere algunas operaciones de procesamiento previo, como la eliminación de ruido, que se puede realizar fácilmente utilizando el filtro gaussiano o los métodos de filtro mediano normal. Estos también están disponibles en OpenCV.
2) Orientación incorrecta de la imagen: debido a una orientación incorrecta, el motor OCR no puede segmentar correctamente las líneas y las palabras de la imagen, lo que proporciona la peor precisión.
3) Presencia de líneas: al realizar una segmentación de palabras o líneas, el motor de OCR a veces también intenta combinar las palabras y las líneas y, por lo tanto, procesar el contenido incorrecto y, por lo tanto, dar resultados incorrectos. También hay otros problemas, pero estos son los básicos.
Esta aplicación de OCR posterior es un ejemplo de caso en el que se pueden aplicar algunos ajustes previos de la imagen y procesamiento posterior en el resultado de OCR para obtener una mejor precisión de OCR.
Lo que fue EXTREMADAMENTE ÚTIL para mí de esta manera son los códigos fuente del proyecto Capture2Text. http://sourceforge.net/projects/capture2text/files/Capture2Text/ .
Por cierto: Felicitaciones a su autor por compartir un algoritmo tan minucioso.
Preste especial atención al archivo Capture2Text / SourceCode / leptonica_util / leptonica_util.c: esa es la esencia de la preprocesión de imágenes para esta utilidad.
Si va a ejecutar los binarios, puede verificar la transformación de la imagen antes / después del proceso en la carpeta Capture2Text / Output /.
La solución mencionada de PS usa Tesseract para OCR y Leptonica para preprocesamiento.
Tres puntos para mejorar la legibilidad de la imagen: 1) Cambie el tamaño de la imagen con altura y ancho variable (multiplique 0.5 y 1 y 2 con altura y ancho de la imagen). 2) Convierta la imagen a formato de escala de grises (blanco y negro). 3) Elimine los píxeles de ruido y deje más claro (Filtre la imagen).
Consulte el siguiente código:
//Resize
public Bitmap Resize(Bitmap bmp, int newWidth, int newHeight)
{
Bitmap temp = (Bitmap)bmp;
Bitmap bmap = new Bitmap(newWidth, newHeight, temp.PixelFormat);
double nWidthFactor = (double)temp.Width / (double)newWidth;
double nHeightFactor = (double)temp.Height / (double)newHeight;
double fx, fy, nx, ny;
int cx, cy, fr_x, fr_y;
Color color1 = new Color();
Color color2 = new Color();
Color color3 = new Color();
Color color4 = new Color();
byte nRed, nGreen, nBlue;
byte bp1, bp2;
for (int x = 0; x < bmap.Width; ++x)
{
for (int y = 0; y < bmap.Height; ++y)
{
fr_x = (int)Math.Floor(x * nWidthFactor);
fr_y = (int)Math.Floor(y * nHeightFactor);
cx = fr_x + 1;
if (cx >= temp.Width) cx = fr_x;
cy = fr_y + 1;
if (cy >= temp.Height) cy = fr_y;
fx = x * nWidthFactor - fr_x;
fy = y * nHeightFactor - fr_y;
nx = 1.0 - fx;
ny = 1.0 - fy;
color1 = temp.GetPixel(fr_x, fr_y);
color2 = temp.GetPixel(cx, fr_y);
color3 = temp.GetPixel(fr_x, cy);
color4 = temp.GetPixel(cx, cy);
// Blue
bp1 = (byte)(nx * color1.B + fx * color2.B);
bp2 = (byte)(nx * color3.B + fx * color4.B);
nBlue = (byte)(ny * (double)(bp1) + fy * (double)(bp2));
// Green
bp1 = (byte)(nx * color1.G + fx * color2.G);
bp2 = (byte)(nx * color3.G + fx * color4.G);
nGreen = (byte)(ny * (double)(bp1) + fy * (double)(bp2));
// Red
bp1 = (byte)(nx * color1.R + fx * color2.R);
bp2 = (byte)(nx * color3.R + fx * color4.R);
nRed = (byte)(ny * (double)(bp1) + fy * (double)(bp2));
bmap.SetPixel(x, y, System.Drawing.Color.FromArgb
(255, nRed, nGreen, nBlue));
}
}
bmap = SetGrayscale(bmap);
bmap = RemoveNoise(bmap);
return bmap;
}
//SetGrayscale
public Bitmap SetGrayscale(Bitmap img)
{
Bitmap temp = (Bitmap)img;
Bitmap bmap = (Bitmap)temp.Clone();
Color c;
for (int i = 0; i < bmap.Width; i++)
{
for (int j = 0; j < bmap.Height; j++)
{
c = bmap.GetPixel(i, j);
byte gray = (byte)(.299 * c.R + .587 * c.G + .114 * c.B);
bmap.SetPixel(i, j, Color.FromArgb(gray, gray, gray));
}
}
return (Bitmap)bmap.Clone();
}
//RemoveNoise
public Bitmap RemoveNoise(Bitmap bmap)
{
for (var x = 0; x < bmap.Width; x++)
{
for (var y = 0; y < bmap.Height; y++)
{
var pixel = bmap.GetPixel(x, y);
if (pixel.R < 162 && pixel.G < 162 && pixel.B < 162)
bmap.SetPixel(x, y, Color.Black);
else if (pixel.R > 162 && pixel.G > 162 && pixel.B > 162)
bmap.SetPixel(x, y, Color.White);
}
}
return bmap;
}
IMAGEN DE ENTRADA
IMAGEN DE SALIDA
Versión de Java para el código de Sathyaraj arriba:
// Resize
public Bitmap resize(Bitmap img, int newWidth, int newHeight) {
Bitmap bmap = img.copy(img.getConfig(), true);
double nWidthFactor = (double) img.getWidth() / (double) newWidth;
double nHeightFactor = (double) img.getHeight() / (double) newHeight;
double fx, fy, nx, ny;
int cx, cy, fr_x, fr_y;
int color1;
int color2;
int color3;
int color4;
byte nRed, nGreen, nBlue;
byte bp1, bp2;
for (int x = 0; x < bmap.getWidth(); ++x) {
for (int y = 0; y < bmap.getHeight(); ++y) {
fr_x = (int) Math.floor(x * nWidthFactor);
fr_y = (int) Math.floor(y * nHeightFactor);
cx = fr_x + 1;
if (cx >= img.getWidth())
cx = fr_x;
cy = fr_y + 1;
if (cy >= img.getHeight())
cy = fr_y;
fx = x * nWidthFactor - fr_x;
fy = y * nHeightFactor - fr_y;
nx = 1.0 - fx;
ny = 1.0 - fy;
color1 = img.getPixel(fr_x, fr_y);
color2 = img.getPixel(cx, fr_y);
color3 = img.getPixel(fr_x, cy);
color4 = img.getPixel(cx, cy);
// Blue
bp1 = (byte) (nx * Color.blue(color1) + fx * Color.blue(color2));
bp2 = (byte) (nx * Color.blue(color3) + fx * Color.blue(color4));
nBlue = (byte) (ny * (double) (bp1) + fy * (double) (bp2));
// Green
bp1 = (byte) (nx * Color.green(color1) + fx * Color.green(color2));
bp2 = (byte) (nx * Color.green(color3) + fx * Color.green(color4));
nGreen = (byte) (ny * (double) (bp1) + fy * (double) (bp2));
// Red
bp1 = (byte) (nx * Color.red(color1) + fx * Color.red(color2));
bp2 = (byte) (nx * Color.red(color3) + fx * Color.red(color4));
nRed = (byte) (ny * (double) (bp1) + fy * (double) (bp2));
bmap.setPixel(x, y, Color.argb(255, nRed, nGreen, nBlue));
}
}
bmap = setGrayscale(bmap);
bmap = removeNoise(bmap);
return bmap;
}
// SetGrayscale
private Bitmap setGrayscale(Bitmap img) {
Bitmap bmap = img.copy(img.getConfig(), true);
int c;
for (int i = 0; i < bmap.getWidth(); i++) {
for (int j = 0; j < bmap.getHeight(); j++) {
c = bmap.getPixel(i, j);
byte gray = (byte) (.299 * Color.red(c) + .587 * Color.green(c)
+ .114 * Color.blue(c));
bmap.setPixel(i, j, Color.argb(255, gray, gray, gray));
}
}
return bmap;
}
// RemoveNoise
private Bitmap removeNoise(Bitmap bmap) {
for (int x = 0; x < bmap.getWidth(); x++) {
for (int y = 0; y < bmap.getHeight(); y++) {
int pixel = bmap.getPixel(x, y);
if (Color.red(pixel) < 162 && Color.green(pixel) < 162 && Color.blue(pixel) < 162) {
bmap.setPixel(x, y, Color.BLACK);
}
}
}
for (int x = 0; x < bmap.getWidth(); x++) {
for (int y = 0; y < bmap.getHeight(); y++) {
int pixel = bmap.getPixel(x, y);
if (Color.red(pixel) > 162 && Color.green(pixel) > 162 && Color.blue(pixel) > 162) {
bmap.setPixel(x, y, Color.WHITE);
}
}
}
return bmap;
}