.\marker\marker\models.py
# 从 marker.cleaners.equations 模块中导入 load_texify_model 函数 from marker.cleaners.equations import load_texify_model # 从 marker.ordering 模块中导入 load_ordering_model 函数 from marker.ordering import load_ordering_model # 从 marker.postprocessors.editor 模块中导入 load_editing_model 函数 from marker.postprocessors.editor import load_editing_model # 从 marker.segmentation 模块中导入 load_layout_model 函数 from marker.segmentation import load_layout_model # 定义一个函数用于加载所有模型 def load_all_models(): # 调用 load_editing_model 函数,加载编辑模型 edit = load_editing_model() # 调用 load_ordering_model 函数,加载排序模型 order = load_ordering_model() # 调用 load_layout_model 函数,加载布局模型 layout = load_layout_model() # 调用 load_texify_model 函数,加载 TeXify 模型 texify = load_texify_model() # 将加载的模型按顺序存储在列表中 model_lst = [texify, layout, order, edit] # 返回模型列表 return model_lst
.\marker\marker\ocr\page.py
import io # 导入io模块 from typing import List, Optional # 导入类型提示相关模块 import fitz as pymupdf # 导入fitz模块并重命名为pymupdf import ocrmypdf # 导入ocrmypdf模块 from spellchecker import SpellChecker # 从spellchecker模块导入SpellChecker类 from marker.ocr.utils import detect_bad_ocr # 从marker.ocr.utils模块导入detect_bad_ocr函数 from marker.schema import Block # 从marker.schema模块导入Block类 from marker.settings import settings # 从marker.settings模块导入settings变量 ocrmypdf.configure_logging(verbosity=ocrmypdf.Verbosity.quiet) # 配置ocrmypdf的日志记录级别为quiet # 对整个页面进行OCR识别,返回Block对象列表 def ocr_entire_page(page, lang: str, spellchecker: Optional[SpellChecker] = None) -> List[Block]: # 如果OCR_ENGINE设置为"tesseract",则调用ocr_entire_page_tess函数 if settings.OCR_ENGINE == "tesseract": return ocr_entire_page_tess(page, lang, spellchecker) # 如果OCR_ENGINE设置为"ocrmypdf",则调用ocr_entire_page_ocrmp函数 elif settings.OCR_ENGINE == "ocrmypdf": return ocr_entire_page_ocrmp(page, lang, spellchecker) else: raise ValueError(f"Unknown OCR engine {settings.OCR_ENGINE}") # 抛出数值错误异常,显示未知的OCR引擎 # 使用tesseract对整个页面进行OCR识别,返回Block对象列表 def ocr_entire_page_tess(page, lang: str, spellchecker: Optional[SpellChecker] = None) -> List[Block]: try: # 获取页面的完整OCR文本页 full_tp = page.get_textpage_ocr(flags=settings.TEXT_FLAGS, dpi=settings.OCR_DPI, full=True, language=lang) # 获取页面的文本块列表 blocks = page.get_text("dict", sort=True, flags=settings.TEXT_FLAGS, textpage=full_tp)["blocks"] # 获取页面的完整文本 full_text = page.get_text("text", sort=True, flags=settings.TEXT_FLAGS, textpage=full_tp) # 如果完整文本长度为0,则返回空列表 if len(full_text) == 0: return [] # 检查OCR是否成功。如果失败,返回空列表 # 例如,如果有一张扫描的空白页上有一些淡淡的文本印记,OCR可能会失败 if detect_bad_ocr(full_text, spellchecker): return [] except RuntimeError: return [] return blocks # 返回文本块列表 # 使用ocrmypdf对整个页面进行OCR识别,返回Block对象列表 def ocr_entire_page_ocrmp(page, lang: str, spellchecker: Optional[SpellChecker] = None) -> List[Block]: # 使用ocrmypdf获取整个页面的OCR文本 src = page.parent # 页面所属文档 blank_doc = pymupdf.open() # 创建临时的1页文档 blank_doc.insert_pdf(src, from_page=page.number, to_page=page.number, annots=False, links=False) # 插入PDF页面 pdfbytes = blank_doc.tobytes() # 获取文档字节流 inbytes = io.BytesIO(pdfbytes) # 转换为BytesIO对象 # 创建一个字节流对象,用于存储 ocrmypdf 处理后的结果 PDF outbytes = io.BytesIO() # let ocrmypdf store its result pdf here # 使用 ocrmypdf 进行 OCR 处理 ocrmypdf.ocr( inbytes, outbytes, language=lang, output_type="pdf", redo_ocr=None if settings.OCR_ALL_PAGES else True, force_ocr=True if settings.OCR_ALL_PAGES else None, progress_bar=False, optimize=False, fast_web_view=1e6, skip_big=15, # skip images larger than 15 megapixels tesseract_timeout=settings.TESSERACT_TIMEOUT, tesseract_non_ocr_timeout=settings.TESSERACT_TIMEOUT, ) # 以 fitz PDF 格式打开 OCR 处理后的输出 ocr_pdf = pymupdf.open("pdf", outbytes.getvalue()) # read output as fitz PDF # 获取 OCR 处理后的文本块信息 blocks = ocr_pdf[0].get_text("dict", sort=True, flags=settings.TEXT_FLAGS)["blocks"] # 获取 OCR 处理后的完整文本 full_text = ocr_pdf[0].get_text("text", sort=True, flags=settings.TEXT_FLAGS) # 确保原始 PDF/EPUB/MOBI 的边界框和 OCR 处理后的 PDF 的边界框相同 assert page.bound() == ocr_pdf[0].bound() # 如果完整文本为空,则返回空列表 if len(full_text) == 0: return [] # 如果检测到 OCR 处理不良,则返回空列表 if detect_bad_ocr(full_text, spellchecker): return [] # 返回文本块信息 return blocks
.\marker\marker\ocr\utils.py
# 导入必要的模块和类 from typing import Optional from nltk import wordpunct_tokenize from spellchecker import SpellChecker from marker.settings import settings import re # 检测 OCR 文本质量是否差,返回布尔值 def detect_bad_ocr(text, spellchecker: Optional[SpellChecker], misspell_threshold=.7, space_threshold=.6, newline_threshold=.5, alphanum_threshold=.4): # 如果文本长度为0,则假定 OCR 失败 if len(text) == 0: return True # 使用 wordpunct_tokenize 函数将文本分词 words = wordpunct_tokenize(text) # 过滤掉空白字符 words = [w for w in words if w.strip()] # 提取文本中的字母数字字符 alpha_words = [word for word in words if word.isalnum()] # 如果提供了拼写检查器 if spellchecker: # 检查文本中的拼写错误 misspelled = spellchecker.unknown(alpha_words) # 如果拼写错误数量超过阈值,则返回 True if len(misspelled) > len(alpha_words) * misspell_threshold: return True # 计算文本中空格的数量 spaces = len(re.findall(r'\s+', text)) # 计算文本中字母字符的数量 alpha_chars = len(re.sub(r'\s+', '', text)) # 如果空格占比超过阈值,则返回 True if spaces / (alpha_chars + spaces) > space_threshold: return True # 计算文本中换行符的数量 newlines = len(re.findall(r'\n+', text)) # 计算文本中非换行符的数量 non_newlines = len(re.sub(r'\n+', '', text)) # 如果换行符占比超过阈值,则返回 True if newlines / (newlines + non_newlines) > newline_threshold: return True # 如果文本中字母数字字符比例低于阈值,则返回 True if alphanum_ratio(text) < alphanum_threshold: # Garbled text return True # 计算文本中无效字符的数量 invalid_chars = len([c for c in text if c in settings.INVALID_CHARS]) # 如果无效字符数量超过阈值,则返回 True if invalid_chars > max(3.0, len(text) * .02): return True # 默认情况下返回 False return False # 将字体标志拆解为可读的形式 def font_flags_decomposer(flags): l = [] # 检查字体标志中是否包含上标 if flags & 2 ** 0: l.append("superscript") # 检查字体标志中是否包含斜体 if flags & 2 ** 1: l.append("italic") # 检查字体标志中是否包含衬线 if flags & 2 ** 2: l.append("serifed") else: l.append("sans") # 检查字体标志中是否包含等宽字体 if flags & 2 ** 3: l.append("monospaced") else: l.append("proportional") # 检查字体标志中是否包含粗体 if flags & 2 ** 4: l.append("bold") # 返回拆解后的字体标志字符串 return "_".join(l) # 计算文本中字母数字字符的比例 def alphanum_ratio(text): # 去除文本中的空格和换行符 text = text.replace(" ", "") text = text.replace("\n", "") # 统计文本中的字母数字字符数量 alphanumeric_count = sum([1 for c in text if c.isalnum()]) # 如果文本长度为0,则返回1 if len(text) == 0: return 1 # 计算字母数字字符比例 ratio = alphanumeric_count / len(text) # 返回变量 ratio 的值 return ratio
Marker 源码解析(二)(2)https://developer.aliyun.com/article/1483803