Python使用pytesseract将图片表格转为Excel的基础实现
将包含表格的图片还原为可编辑的Excel电子表格,核心路径分为三步:首先借助计算机视觉工具定位表格的网格边框并切分出独立的单元格区域,然后利用光学字符识别(OCR)引擎提取每个单元格内的文本,最后按照行列坐标重新构建二维数据结构并导出为.xlsx文件。本文将使用Python结合OpenCV、pytesseract以及Pandas,讲解如何完成这一基础流程。
环境依赖与准备工作
在开始编写代码前,需要确保系统中已经安装了Tesseract OCR引擎程序以及对应的Python封装库。pytesseract本质上是Tesseract命令行工具的封装,不能脱离独立的引擎本体工作。
1. 系统级Tesseract引擎安装
- Windows:下载官方推荐的安装包并执行安装,记录下安装路径(默认通常为
C:\Program Files\Tesseract-OCR\tesseract.exe)。如需识别中文,请在安装时勾选中文语言包(chi_sim)。 - macOS:通过Homebrew运行
brew install tesseract tesseract-lang。 - Linux (Ubuntu/Debian):通过终端运行
sudo apt-get install tesseract-ocr tesseract-ocr-chi-sim。
2. Python库依赖安装
在本地终端或虚拟环境中执行以下命令安装所需的三方库:
pip install pytesseract opencv-python pandas openpyxl
核心实现步骤与代码解析
如果直接对包含表格的整张图片调用OCR,识别引擎通常会将同一行甚至相邻单元格的文本混合拼接,彻底丢失原有的行列网格关系。稳妥的基础实现做法是:借助OpenCV识别水平线与垂直线,提取出所有单元格的边界矩形,再分别识别每个单元格内容。
完整实现代码
下面给出一段可直接运行的基础实现脚本:
import cv2
import numpy as np
import pytesseract
import pandas as pd
# Windows环境下若未加入环境变量,需显式指定可执行文件路径
# pytesseract.pytesseract.tesseract_cmd = r"C:\Program Files\Tesseract-OCR\tesseract.exe"
def image_table_to_excel(image_path, output_excel_path):
# 1. 读取图像并转换为灰度图
img = cv2.imread(image_path)
if img is None:
raise FileNotFoundError(f"无法读取图片: {image_path}")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 2. 自适应二值化 (反色:黑底白线,便于形态学计算)
binary = cv2.adaptiveThreshold(
~gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, -2
)
# 3. 构建水平与垂直形态学卷积核
scale = 30
horizontal_size = max(1, binary.shape[1] // scale)
h_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (horizontal_size, 1))
vertical_size = max(1, binary.shape[0] // scale)
v_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, vertical_size))
# 提取横线与竖线
h_lines = cv2.morphologyEx(binary, cv2.MORPH_OPEN, h_kernel, iterations=2)
v_lines = cv2.morphologyEx(binary, cv2.MORPH_OPEN, v_kernel, iterations=2)
# 合成完整表格网格
table_mask = cv2.add(h_lines, v_lines)
# 4. 查找单元格轮廓
contours, _ = cv2.findContours(table_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
boxes = []
min_area = 100 # 过滤微小噪点轮廓
for c in contours:
x, y, w, h = cv2.boundingRect(c)
if w * h > min_area and w < img.shape[1] * 0.98 and h < img.shape[0] * 0.98:
boxes.append((x, y, w, h))
if not boxes:
print("未检测到有效单元格")
return
# 5. 按照坐标对单元格进行行列归类排序
# 先按 Y 坐标排序,以大致高度聚合为行
boxes.sort(key=lambda b: b[1])
rows = []
current_row = []
row_height_threshold = 12 # Y轴容差阈值
last_y = boxes[0][1]
for b in boxes:
if abs(b[1] - last_y) > row_height_threshold:
# 同一行内的单元格按 X 坐标自左向右排序
current_row.sort(key=lambda b: b[0])
rows.append(current_row)
current_row = [b]
last_y = b[1]
else:
current_row.append(b)
# 更新基准Y为均值以避免累积漂移
last_y = int(np.mean([item[1] for item in current_row]))
if current_row:
current_row.sort(key=lambda b: b[0])
rows.append(current_row)
# 6. 逐单元格截取并执行OCR识别
table_data = []
# PSM 6: 单一文本块; PSM 7: 单行文本
custom_config = r"--psm 6"
for r_idx, row in enumerate(rows):
row_data = []
for (x, y, w, h) in row:
# 留出2像素内边距以剔除表格线干扰
pad = 2
cell_roi = gray[y+pad : y+h-pad, x+pad : x+w-pad]
if cell_roi.size == 0:
cell_roi = gray[y:y+h, x:x+w]
# 针对单格区域使用pytesseract识别
text = pytesseract.image_to_string(cell_roi, lang='chi_sim+eng', config=custom_config)
cleaned_text = text.strip().replace("\n", " ")
row_data.append(cleaned_text)
table_data.append(row_data)
# 7. 转为DataFrame并导出Excel
df = pd.DataFrame(table_data)
df.to_excel(output_excel_path, index=False, header=False)
print(f"转换完成,已保存至: {output_excel_path}")
if __name__ == "__main__":
image_table_to_excel("sample_table.png", "output_table.xlsx")
两种识别策略的差异与适用场景
在处理图片中的表格时,开发者通常有两种思路:一是直接全图OCR通过字符坐标进行空间聚类,二是通过形态学锁定网格线进行ROI单元格裁剪识别。在一致的前提条件下,二者的关键差异如下:
- 全图OCR坐标聚类:直接对整图调用带有定位信息的OCR(如
image_to_data),根据文字框的坐标计算文本行与列边界。优点是对无边框表格、虚线表格依然有一定适应性;缺点是如果某一列存在空单元格,容易产生错位对齐偏差。 - 网格形态学切分ROI(本文方案):依赖图像中的物理边框线提取闭合单元格矩形。优点是单元格坐标完全由物理表格线锁定,即使某一单元格为空也不会影响后续行列排布;缺点是对无边框表格或断线严重的表格无法正常提取轮廓。
常见问题与调试建议
注意:
- 边框线被误识别为字符:在送入pytesseract识别前,务必对单元格矩形切片设置2~3像素的内边距(padding),避免残留的黑线被Tesseract识别为下划线、减号或字母“l”。
- PSM模式的选择:单元格内文字通常简短或仅有一两行,使用默认的PSM 3(全自动页面分割)可能导致漏识别。建议传入
--psm 6(假设为单一文本块)或--psm 7(视为单文本行)。 - 中文与数字混合识别:若表格包含中英文,参数应配置为
lang='chi_sim+eng',确保对应字库文件均已位于tessdata目录下。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













