登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

视觉问答图片怎么先做区域裁剪:OCR 框选、坐标映射与多轮复核

来源:17golang原创

时间:2026-08-30 03:27:28 425浏览 收藏

视觉问答里,整张长图并不总是信息越全越好。比如一张巡检单同时有标题、表格和备注,直接送入模型后,真正要核对的“设备编号”只占很小一块,模型既容易漏字,也很难解释自己看的是哪一块。更稳的处理是先用 OCR 找到文字框,裁出目标区域,再把裁剪结果交给视觉模型复核。

先定位、再裁剪、后复核。OCR 返回的 boundingPoly 只负责提供候选位置,应用必须把坐标限制在原图范围内,并用 verifyCrop 再识别一次,确认裁剪没有偏移。

要点速览
  • 稀疏文字可从 TEXT_DETECTION 开始,密集文档需要保留 page、block、paragraph、word 层级。
  • 归一化坐标必须乘以原图宽高,裁剪前还要执行边界夹紧,避免负数或越界。
  • cropRect 只生成候选区域,真正送入视觉问答前要通过 verifyCrop 二次核对。
  • 裁剪不是“把图片缩小”这么简单:要同时记录原图尺寸、框坐标、目标词和复核文本,后续才能追责。

先判断:整图失败往往不是模型能力不够

长图进入视觉模型后,文字会因为缩放变小;如果请求还附带多个问题,注意力会被图中无关区域分散。先裁剪的价值在于把“找区域”和“回答问题”拆成两个可检查的阶段。

Google Cloud Vision 的 OCR 接口会返回识别文本和边界框;针对密集文档,还能得到 page、block、paragraph、word 的结构化结果。AWS Textract 的 BoundingBox 则把 LeftTopWidthHeight 表示为相对整页的比例。两类接口的字段命名不同,但工程上的第一步一致:保留原始坐标,不要让模型自行猜区域。

用 OCR 框选目标,再把坐标变成像素矩形

下面的 Go 示例假设 OCR 适配层已经把服务返回结果统一成 TextBox。它不绑定某一家云服务,重点是把边界和目标词一起保留下来:

type TextBox struct {
    Text string
    Left, Top, Width, Height float64 // 归一化坐标,范围 0 到 1
}

type Rect struct { X, Y, W, H int }

func detectText(boxes []TextBox, target string) (TextBox, bool) {
    for _, box := range boxes {
        if strings.Contains(box.Text, target) {
            return box, true
        }
    }
    return TextBox{}, false
}

func cropRect(box TextBox, imageW, imageH int, pad int) (Rect, error) {
    if imageW  imageW { w = imageW - x }
    if y+h > imageH { h = imageH - y }
    return Rect{X: x, Y: y, W: w, H: h}, nil
}

这里的关键不是浮点数乘法,而是边界策略。LeftTop 以左上角为原点,乘上真实宽高后才是像素位置;留白 pad 可以把一行字的上下文带进来,但不能让矩形越过原图。生产代码还应把原图宽高和原始 OCR 响应存进审计记录。

detectText 找到设备编号后,boundingPoly 经过坐标映射进入 cropRect 的视觉问答裁剪数据流

只裁出一块还不够,必须确认裁剪没有偏移

最常见的隐性错误是宽高顺序写反、把百分比当像素,或者对已经旋转的图片仍按原方向计算。裁剪结果看起来“有内容”,并不代表目标词还在里面。

func verifyCrop(crop image.Image, target string, recognize func(image.Image) []TextBox) error {
    boxes := recognize(crop)
    for _, box := range boxes {
        if strings.Contains(box.Text, target) {
            return nil
        }
    }
    return fmt.Errorf("target %q missing after crop", target)
}

func prepareVisionInput(src image.Image, boxes []TextBox, target string) (image.Image, error) {
    box, ok := detectText(boxes, target)
    if !ok { return nil, fmt.Errorf("target %q not found", target) }
    rect, err := cropRect(box, src.Bounds().Dx(), src.Bounds().Dy(), 16)
    if err != nil { return nil, err }
    crop := src.(interface{ SubImage(image.Rectangle) image.Image }).SubImage(
        image.Rect(rect.X, rect.Y, rect.X+rect.W, rect.Y+rect.H))
    if err := verifyCrop(crop, target, recognize); err != nil { return nil, err }
    return crop, nil
}

verifyCrop 是一道故意保守的门槛:二次 OCR 找不到目标词,就返回错误,不把这个区域继续交给视觉问答。prepareVisionInput 也把“目标不存在”“坐标无效”“复核失败”区分开,日志里可以判断究竟是 OCR 漏检还是坐标映射出错。图中的 target present 表示复核通过,target missing 表示应当拒绝这次裁剪。

cropRect 生成候选区域后,verifyCrop 将结果分成目标存在与目标缺失两条状态路径

把误裁剪风险变成可审计的输入记录

视觉模型的答案很难单独证明它看到了什么,所以每次裁剪至少要留下四类字段:原图尺寸、目标词、OCR 框和裁剪矩形。若 OCR 服务返回旋转角或多边形,不要静默压成一个轴对齐矩形;要在记录中保留原始多边形,并注明当前适配层采用的近似方式。

对于敏感图片,原图和裁剪图的保存周期也应分开。业务只需要回答设备编号时,短期保留裁剪区域、长期保留哈希和框坐标,通常比永久保存整张巡检单更容易控制访问范围。

上线前的三组回归样本

  • 边界样本:目标词贴近左边、右边和底边,确认 cropRect 的夹紧逻辑不会产生零宽矩形。
  • 比例样本:准备一张宽高比明显不同的图片,专门检查 Left/Top 是否被错误地当成像素。
  • 旋转样本:文字倾斜或图片带方向信息时,记录 OCR 的 polygon/rotation 结果,并检查二次识别仍能找到目标词。

验收时不要只看视觉模型最后答对没有。先比对原始 OCR 文本、裁剪后 OCR 文本和模型输入的图片尺寸;三者能对齐,问题才有继续定位的抓手。

常见问题

所有图片都应该先做 OCR 裁剪吗?

不应该。目标区域很大、整图上下文不可替代,或 OCR 质量明显不稳定时,整图输入更合适。裁剪应由目标词命中率和复核结果共同决定。

为什么归一化坐标不能直接传给裁剪函数?

常见图像裁剪 API 使用像素矩形。必须乘以原图宽高,并处理取整、留白和边界夹紧,否则不同尺寸图片会得到完全不同的区域。

二次 OCR 失败但人眼能看到目标,怎么办?

先保留失败样本,检查旋转、压缩和裁剪留白,再考虑扩大区域或切换文档识别模式。不要直接跳过复核,否则一次坐标错误会被误认为模型回答错误。

总结

稳定的视觉问答输入不是把原图无条件缩小,而是把 OCR 定位、像素坐标映射、边界约束和二次复核串成一条可审计的数据路径。detectText 负责找到目标,cropRect 负责生成安全矩形,verifyCrop 负责拦住偏移结果;这三步通过后,再把裁剪图交给视觉模型,排错会清楚很多。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>