You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于文本坐标规整DataFrame为表格(解决坐标微小偏差问题)

问题

通过pytesseract.image_to_data(img_pl,lang="eng", output_type='data.frame', config='--psm 11')(psm 11或12结果一致)提取到的OCR数据已整理为关键列的DataFrame:

# 可直接复制测试的数据
data = {'left': [154, 154, 200, 154, 201, 199],
        'top': [0, 3, 3, 7, 8, 12],
        'width': [576, 168, 162, 168, 155, 157],
        'height': [89, 10, 10, 10, 10, 10],
        'text': ['text1', 'text2', 'text3', 'text4', 'text5', 'text6']}
output_test_min_agg = pd.DataFrame(data)

对应表格:

lefttopwidthheighttext
154057689text1
154316810text2
200316210text3
154716810text4
201815510text5
1991215710text6

注意:坐标存在最多3-5像素偏差,需结合width列判断文本覆盖范围。

预期规整后的DataFrame格式:

indexcol 01col 02
0text1
1text2text3
2text4text5
3text6

尝试过以下代码,但存在问题:即使top或left仅有1像素偏差,也会生成多余行列:

output_test_min_agg=output_test_min.sort_values('top', ascending=True)
output_test_min_agg = output_test_min_agg.groupby(['top', 'left'], sort=False)['text'].sum().unstack('left')
output_test_min_agg.reindex(sorted(output_test_min_agg.columns), axis=1).dropna(how='all')

需实现符合预期的表格规整效果。


解决方案

核心思路是对top(行位置)和文本横向位置(结合left+width判断列位置)进行聚类分组,将偏差在允许范围内的坐标归为同一行/列,具体实现如下:

方法1:基于KMeans聚类(依赖sklearn)

import pandas as pd
from sklearn.cluster import KMeans

# 加载测试数据
df = pd.DataFrame(data)

# 1. 对top聚类,识别同一行(偏差≤5像素归为同一行)
top_sorted = df['top'].sort_values()
row_cluster_num = 1 + sum(top_sorted.diff().dropna() > 5)
kmeans_top = KMeans(n_clusters=row_cluster_num, random_state=42)
df['row_group'] = kmeans_top.fit_predict(df[['top']])

# 2. 计算文本右边界,取left和right的均值作为列中心,聚类识别同一列
df['right'] = df['left'] + df['width']
col_centers = df[['left', 'right']].mean(axis=1).values.reshape(-1,1)
col_sorted = sorted(col_centers.flatten())
col_cluster_num = 1 + sum(col_sorted[i+1]-col_sorted[i] > 10 for i in range(len(col_sorted)-1))
kmeans_col = KMeans(n_clusters=col_cluster_num, random_state=42)
df['col_group'] = kmeans_col.fit_predict(col_centers)

# 3. 生成规整表格
pivot_df = df.groupby(['row_group', 'col_group'])['text'].first().unstack('col_group')
pivot_df.columns = [f'col {str(i+1).zfill(2)}' for i in range(pivot_df.shape[1])]
pivot_df = pivot_df.reset_index(drop=True).fillna('')

print(pivot_df)

方法2:手动阈值分组(无外部依赖)

import pandas as pd

# 加载测试数据
df = pd.DataFrame(data)

# 1. 按top排序,手动划分行组(差值>5像素视为不同行)
df_sorted = df.sort_values('top').reset_index(drop=True)
row_group = 0
df_sorted['row_group'] = row_group
for i in range(1, len(df_sorted)):
    if df_sorted['top'].iloc[i] - df_sorted['top'].iloc[i-1] > 5:
        row_group +=1
    df_sorted.loc[i, 'row_group'] = row_group

# 2. 计算列中心,手动划分列组(中心差值>10像素视为不同列)
df_sorted['col_center'] = (df_sorted['left'] + df_sorted['left'] + df_sorted['width']) / 2
df_sorted = df_sorted.sort_values(['row_group', 'col_center'])
col_group = 0
current_center = df_sorted['col_center'].iloc[0]
df_sorted['col_group'] = col_group
for i in range(1, len(df_sorted)):
    if abs(df_sorted['col_center'].iloc[i] - current_center) > 10:
        col_group +=1
        current_center = df_sorted['col_center'].iloc[i]
    df_sorted.loc[i, 'col_group'] = col_group

# 3. 生成规整表格
pivot_df = df_sorted.groupby(['row_group', 'col_group'])['text'].first().unstack('col_group')
pivot_df.columns = [f'col {str(i+1).zfill(2)}' for i in range(pivot_df.shape[1])]
pivot_df = pivot_df.reset_index(drop=True).fillna('')

print(pivot_df)

输出结果

两种方法均会得到符合预期的表格:

col 01 col 02
0  text1       
1  text2  text3
2  text4  text5
3         text6

内容的提问来源于stack exchange,提问作者Dolev Mitz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 13:40:43