同一份带表格的图片,喂给看图问答和喂给表格抽取,需要的输入并不一样:看图问答要的是关键区域的像素足够清楚,表格抽取要的是行列结构在处理过程中别被破坏。所以预处理不能只做一套。比较稳妥的顺序是先把素材固定下来(原图、提高分辨率的版本、裁剪出表格区域的版本),让两条链路在同一批素材上各跑一遍,再按输出里出现的“看不清”和“串行错位”两类失败分别调参数,而不是先猜一套参数然后全量套用。
判断方向:看图问答链路优先保“局部可读”,做法是裁剪关键区域后放大;表格抽取链路优先保“行列可对齐”,做法是保留结构(图像或对齐文本),不要随手压成一段流水文本。适用场景是同一份带表格材料既要问图又要抽表。操作上先固定三份素材并记录尺寸,两条链路各留请求日志和失败样本。验证靠逐字比对模型输出与目视结果、检查 TSV 每行列数是否一致。边界:模型接口字段以实际接入为准,参数起点值需要结合素材实测调整。
把一份带表格的图片做成两种输入
先固定素材,后面才有可比性。三份素材用途不同:原图只留档、不下结论;提高分辨率版本用于整图提问和表格抽取;裁剪版本只用于局部问答。文件名固定成 src_orig / src_up2x / src_crop_table,后续请求日志里直接写文件名,避免“用的是哪一版图”说不清。
# 1) 原图留档:不缩放,只重编码
magick input.jpg -strip -quality 95 src_orig.jpg
# 2) 提高分辨率版本:整图放大,表格线会明显一些
magick input.jpg -colorspace sRGB -resize 200% src_up2x.png
# 3) 裁剪版本:只保留表格区域,坐标按你图里的实际位置改
magick input.jpg -crop 1400x900+320+260 +repage -resize 200% src_crop_table.png
# 记录三份素材实际尺寸,写进素材清单
identify -format "%f\t%wx%h\n" src_orig.jpg src_up2x.png src_crop_table.png
裁剪坐标不用一次对,先按目视估一个矩形,跑完看图确认是否把表头和数据行都框进去了;框漏了表头,后面抽取必然少一行。
看图问答链路:先确认关键区域是否可读
这一步的目的是排除“因为图太糊导致答非所问”。裁剪范围建议按问题来定:问某个单元格就裁到单元格附近带一点上下文,问某一行就裁整行,不要既裁又缩,缩回去等于白裁。放大倍数从 2 倍起,放大后确认字边不发虚再加。
针对局部提问的请求骨架如下,字段名请按实际接口替换,图片可以是 URL 也可以是 base64:
{
"model": "sense-nova-6.8-flash",
"messages": [{
"role": "user",
"content": [
{"type": "text", "text": "只回答图中第 3 行第 2 列单元格的内容,原样输出文字,不要解释。看不清就回答『看不清』。"},
{"type": "image_url", "image_url": {"url": "data:image/png;base64,<src_crop_table.png 的编码>"}}
]
}],
"temperature": 0
}
读不清时的输出表现通常有几种,可以直接当成失败信号:一是把相邻两格内容拼在一起返回;二是给“表头看起来是……”这类带猜的表述;三是明确回答看不清或无法辨认;四是内容对但列位置错。把这四种分别记在日志里,第一种和第四种最容易被误判成“模型答对了”。
表格抽取链路:保留结构还是转成文本
判断标准很简单:哪种输入更容易得到行列对齐的结果,就用哪种。一般来说,表线清晰、单元格内容短的表,直接给图像并要求输出 TSV 更省事;表线缺失、单元格里有换行或合并单元格的,先把 OCR 结果按制表符粗略对齐成文本,再让模型只做“结构还原”,比让它同时做识别和排版更可控。
# 输入 A:图像直出结构化
POST /v1/chat/completions
{
"model": "sense-nova-6.8-flash",
"messages": [{"role": "user", "content": [
{"type": "text", "text": "把表格转成 TSV:第一行是表头,行内用制表符分隔,单元格内换行用空格替代,不要合并单元格,跨行跨列处按左上角内容填一次并在值后加 *。"},
{"type": "image_url", "image_url": {"url": "<src_up2x.png 的 URL 或 base64>"}}
]}],
"temperature": 0
}
# 输入 B:文本还原
{"role": "user", "content": "下面是 OCR 文本,行用换行保留、列用制表符粗略对齐。请还原成 TSV,并把无法对齐的行单独列在末尾并注明行号:\n<粘贴 OCR/TSV 文本>"}
输出拿到后先机械核对行数和列数,再谈内容对不对:
# 每行列数分布,列数不齐的行就是错位候选
awk -F'\t' '{print NF}' out.tsv | sort | uniq -c
# 行数与目视表体行数是否一致
wc -l out.tsv
错位样本按固定格式记录,例如:行 5、列 3、输入形式 图像、现象 单元格内容右移一列、可能原因 该行表线断裂导致模型按空白切分。记录粒度到行列,后面调参才有依据。
比对两条链路的输出差异并记下失败样本
同一份素材在两种任务下的薄弱环节往往不重合:看图问答失败常出现在小字号和浅色字上,表格抽取失败常出现在跨行跨列和空单元格上。对比表建议每次只放一个素材版本,避免把变量混在一起。
| 任务 | 输入形式 | 正确项 | 错位或漏项 | 可能原因 |
|---|---|---|---|---|
| 看图问答 | src_orig 原图 | 表头文字 | 小字号单元格答成相邻内容 | 原图分辨率不足,字边粘连 |
| 看图问答 | src_crop_table 裁剪放大 | 局部单元格 | 裁剪框边缘切掉半个字 | 裁剪坐标偏紧 |
| 表格抽取 | src_up2x 图像 | 前几行列对齐 | 跨行单元格内容重复填入 | 合并单元格未声明处理规则 |
| 表格抽取 | OCR 文本 | 纯文字单元格 | 数字列被空格切开成两列 | OCR 列对齐不稳定 |
这张表的作用是定位“该改预处理还是该改提示词”:错位集中在同一列,多半是输入结构问题;答非所问集中在同一区域,多半是分辨率问题。
把预处理参数按任务拆成两套配置
两套配置分开命名、分开落盘,请求日志各写各的,出问题时能一眼看出是哪个链路的输入不合格。下面的取值只是起点,需要结合你自己的素材实测后调整,不要直接当结论。
# qa.env —— 看图问答链路
QA_SOURCE=src_crop_table.png # 局部提问用裁剪版,整图提问换 src_up2x.png
QA_MAX_EDGE=1600 # 长边上限,超过先缩再送
QA_UPSCALE=2.0
QA_ASK_SCOPE=single_cell # single_cell | region | whole_image
QA_UNCLEAR_TOKEN=看不清
QA_LOG=logs/qa.jsonl
# table.env —— 表格抽取链路
TABLE_SOURCE=src_up2x.png
TABLE_MAX_EDGE=2400
TABLE_UPSCALE=1.5
TABLE_INPUT_MODE=image # image | text
TABLE_OUTPUT_FORMAT=tsv
TABLE_ALLOW_MERGE=false
TABLE_LOG=logs/table.jsonl
各自的验证命令与检查清单:
# 确认送到模型的就是配置里那份素材
identify -format "%f %wx%h\n" "$QA_SOURCE" "$TABLE_SOURCE"
- 看图问答:同一问题在原图、放大版、裁剪版各问一次,三次结果不一致时先怀疑输入而不是模型;回答是否出现“可能、看起来”等猜测词;日志里是否记下素材文件名和坐标。
- 表格抽取:TSV 每行列数是否一致;表头行是否与目视一致;合并单元格是否有统一处理规则;漏行的行号是否已记进失败样本。
- 两条链路共用的检查:请求里带的图片尺寸与配置是否对得上;
temperature是否设为 0 以便复现;同一条请求是否保留原始返回以便比对。
参数改动一次只动一项,改完在日志里留一行说明动了什么,否则前后两次失败样本没法归因。素材本身有问题(例如扫描歪斜、反光遮字)时,预处理参数再怎么调也只是绕过去,这种情况应回到取图环节处理。