¿Como hacer un OCR de imágenes contenidas en un pdf?
Muchos pdfs proceden de escaner o de una conversión de tiff multipágina con el mismo origen, siendo imágenes embebidas donde el texto son pixels y no caracteres.
En esos casos, la extracción del texto por procedimientos estandar no dará el resultado esperado porque no hay texto como tal.
En la entrada anterior, Extraer texto de un pdf con Python, vimos como instalar y comenzar a trabajar con pdfminer3k extrayendo textos de un pdf.
Si bien únicamente accedíamos a objetos propiamente de texto, los LTTextBox y LTTextLine.
Localizar y extraer imágenes del pdf con pdfminer3k
Vamos a combinar dos librerías que ya conocemos, pdfminer3k y tesseract.
Recordaréis que dejamos pendiente de implementar el tratamiento de LTFigure.
for lt_obj in layout: if isinstance(lt_obj, LTTextBox) or isinstance(lt_obj, LTTextLine): # aqui hacemos la magia: spagetxt = lt_obj.get_text().strip() + " " ... elif isinstance(lt_obj, LTFigure): print("LTFigure, pte implementar!")
Bien, solo tenemos que implementar la recuperación de la imagen para su ocr.
Incluimos las siguientes lineas en la función principal.
elif isinstance(lt_obj, LTFigure): spagetxt="" for im in lt_obj: if isinstance(im, LTImage): try: imdata = im.stream.get_data() except: imdata = im.stream.get_rawdata() fnameimg=saveimg(imdata) if(fnameimg!=""): spagetxt=gettxt(fnameimg) #obtiene texto sin generar archivo txt if(spagetxt!=""): btxt=True fptxt.write(spagetxt) print(spagetxt) os.remove(fnameimg)
La función saveimg, crea un archivo temporal por cada LTimage y devuelve el nombre.
def saveimg(imgdata): if imgdata is None: print("imgdata esta vacio") return "" if not imgdata.startswith(b'\xff\xd8\xff\xe0'): print("imgdata no es valido") return "" fd, fname = tempfile.mkstemp(prefix='ncm_', suffix='.jpg') try: with open(fname, 'wb') as f: f.write(imgdata) except: return "" finally: os.close(fd) print("return " + fname) return fname
Implementación del OCR con tesseract
Añadimos los siguientes import en la cabecera.
import tempfile, os import cv2 import pytesseract
La función gettxt espera como parámetro el path de una imagen y devuelve el texto extraido de ella.
def fileexist(spath): my_file = Path(spath) if my_file.is_file(): return True else: return False def gettxt(spath): try: if not (fileexist(spath)): return "" img = cv2.imread(spath) sret="" sret=pytesseract.image_to_string(img) print("ocr ok!") return sret except Exception as e: print("Error: %s" % (e)) return "" except: print("Error desconocido") return ""
El código completo
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Created on Thu Sep 13 10:55:00 2018 dge @author: altaruru """ import tempfile, os import cv2 import pytesseract from pathlib import Path from pdfminer.pdfparser import PDFParser, PDFDocument from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter from pdfminer.converter import PDFPageAggregator from pdfminer.layout import LAParams, LTTextBox, LTTextLine, LTImage, LTFigure def fileexist(spath): my_file = Path(spath) if my_file.is_file(): return True else: return False def gettxt(spath): try: if not (fileexist(spath)): return "" img = cv2.imread(spath) sret="" sret=pytesseract.image_to_string(img) print("ocr ok!") return sret except Exception as e: print("Error: %s" % (e)) return "" except: print("Error desconocido") return "" def saveimg(imgdata): if imgdata is None: print("imgdata esta vacio") return "" if not imgdata.startswith(b'\xff\xd8\xff\xe0'): print("imgdata no es valido") return "" fd, fname = tempfile.mkstemp(prefix='ncm_', suffix='.jpg') try: with open(fname, 'wb') as f: f.write(imgdata) except: return "" finally: os.close(fd) print("return " + fname) return fname def pdf2txt(pdfname, txtname): btxt=False try: fp = open(pdfname, 'rb') parser = PDFParser(fp) doc = PDFDocument() parser.set_document(doc) doc.set_parser(parser) doc.initialize('') rsrcmgr = PDFResourceManager() laparams = LAParams() laparams.char_margin = 1.0 laparams.word_margin = 1.0 device = PDFPageAggregator(rsrcmgr, laparams=laparams) interpreter = PDFPageInterpreter(rsrcmgr, device) ncount=0 print("pdf2txt %s..." % pdfname) # informa por consola del nombre de archivo # abre archivo de texto para la salida fptxt = open(txtname, 'w') # recorre el documento procesando cada página for page in doc.get_pages(): interpreter.process_page(page) layout = device.get_result() # recorre la página procesando cada objeto for lt_obj in layout: if isinstance(lt_obj, LTTextBox) or isinstance(lt_obj, LTTextLine): spagetxt = lt_obj.get_text().strip() + " " if(spagetxt!=""): btxt=True fptxt.write(spagetxt) #print(spagetxt) elif isinstance(lt_obj, LTFigure): spagetxt="" for im in lt_obj: if isinstance(im, LTImage): try: imdata = im.stream.get_data() except: imdata = im.stream.get_rawdata() fnameimg=saveimg(imdata) if(fnameimg!=""): spagetxt=gettxt(fnameimg) if(spagetxt!=""): btxt=True fptxt.write(spagetxt) print(spagetxt) os.remove(fnameimg) ncount+=1 print("end") fptxt.closed fp.closed except Exception as e: print("Error: %s" % (e)) return btxt pdf2txt("/home/nuse/Documentos/src/pysrc/imgs/Computer-Vision-Resources.pdf","/home/nuse/Documentos/src/pysrc/imgs/Computer-Vision-Resources.txt")
Feliz dia!