ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

大图识别不清?vision-exp-tile切片识图方案,让AI看清每一个细节

大图识别不清?vision-exp-tile切片识图方案,让AI看清每一个细节 最近在业务里频繁遇到一类问题拿一张尺寸很大的 UI 设计图、长截图或者工程图纸丢给多模态大模型识别模型经常回复“图片分辨率不足文字无法辨认”或者干脆漏掉右下角的一大块关键信息。反复调整 Prompt 也没有太大改善最后定位到根因不是模型能力不够而是我们给模型的“看”的方式不对。针对这个问题我把切片识图的思路整理成了一个可复用的工具方案也就是标题里说的 vision-exp-tile 智能识图插件。核心思路很直接把大图按 800×800 切成小块再结合全局缩略图和文本坐标标注一起交给 AI让模型既能看到全貌又能看清每个局部细节。这篇文章会完整拆解这套方案的设计思路、环境准备、核心代码、排错清单和工程建议帮你少走弯路。1. 大图识别的痛点与切片方案1.1 为什么大图总是识别不清先来看一个常见现象。同样一张图片人类可以随意缩放、局部放大但多模态模型不一样。为了控制计算成本大多数视觉语言模型在真正“看”图片之前会先把输入图片缩放或者切片成固定尺寸再经过视觉编码器提取特征。这意味着什么假设你传入一张 4000×3000 的工程图纸模型内部可能先把长边缩到 1024 甚至更小。原本清晰的 8 号字体、细密的表格线、图纸角落的批注在缩放过程中直接被丢弃了。模型拿到的是一张“缩得很小的全图”自然会出现幻觉或者诚实地告诉你图片太小看不清。很多人遇到这种情况第一反应是换更大型号的模型。但换个更大的模型同样受限于输入分辨率上限。真正要解决的是输入侧的信息密度问题。1.2 切片方案的基本思路既然模型一次看全图会缩小看不清楚细节那我们反过来操作把大图切成很多小图每次给模型看一块。同时为了避免模型“只见树木不见森林”我们再补一张全图缩略图让模型知道每一块在整个图中的空间位置。以 vision-exp-tile 插件为例它的完整处理流程分成四步将原图生成一张缩略图保留全局空间布局。将原图按固定尺寸默认 800×800切分为多个切片切片之间允许重叠。将缩略图、切片图、以及每个切片对应的原图坐标范围一起组装为模型请求。模型识别出目标后把切片坐标换算回原图坐标得到准确的定位结果。这套方案本质上是对多模态模型输入的一种“分而治之”策略。如果你在开发 AI 识图应用、OCR 工具、自动化测试平台或者想把大图喂给 GPT-4o、Qwen-VL、Claude 等视觉模型这个思路都非常实用。1.3 适用场景这套方案主要适合以下几类场景长截图、聊天记录截图、网页全屏截图的文字提取与信息结构化。大尺寸 UI 设计稿的控件定位与自动化测试脚本生成。工程图纸、电路图、架构图中的小字识别与目标查找。海报、表格、票据等混合排版图片的精细化识别。需要对识别目标进行“像素级定位”的业务比如自动化点击、框选标注。2. 环境准备与版本说明在动手实现 vision-exp-tile 插件之前我们先准备好运行环境。如果你已经熟悉 Python 和图像处理库可以直接跳过这节但建议还是看一下版本兼容说明。2.1 运行环境本文示例以 Python 3.9 为基础环境。图像处理使用 Pillow模型调用部分以 OpenAI 兼容接口为例。具体版本需要根据你的项目实际情况调整本文重点演示配置思路而不是绑定某个固定版本。建议准备以下依赖Python 3.9 或更高版本。Pillow 9.1.0 或更高版本用于图像缩放、裁剪、编码。openai 或对应视觉模型厂商的 SDK用于调用多模态模型。requests如果你不想引入完整 SDK也可以用 HTTP 方式直接调用接口。安装命令如下pip install Pillow openai requests如果你的网络环境特殊或者不想使用 pip也可以使用 condaconda install pillow conda install openai2.2 示例项目结构为了让后续代码更有条理建议按下面的结构组织项目vision-exp-tile/ ├── images/ │ └── demo.png # 测试用大图 ├── output/ │ ├── thumbnail.jpg # 生成的缩略图 │ └── tiles/ # 切片输出目录 │ ├── tile_0.jpg │ ├── tile_1.jpg │ └── ... ├── vision_exp_tile.py # 核心插件逻辑 ├── call_model.py # 模型调用示例 └── requirements.txt # 依赖清单其中vision_exp_tile.py负责图片切片、缩略图生成、坐标换算call_model.py负责组装消息、调用视觉模型、解析返回结果。3. 核心原理拆解在写代码之前先把 vision-exp-tile 的几个核心原理讲清楚。理解了这些设计点你才能根据实际业务调整参数而不是复制代码后遇到问题不知道怎么改。3.1 切片尺寸为什么选 800×800很多人会问为什么不直接用 1024×1024或者干脆用 512×512这个尺寸选择本质上是一个平衡点。如果切片尺寸过大模型内部依然会对图片做缩放局部细节可能仍然丢失。而且单张图片占用的视觉 token 会成倍增加。如果切片尺寸过小同样一张大图会被切成几十甚至上百块请求体变得很长模型处理时间变长而且目标物体容易横跨多块切片导致识别结果被拆散。800×800 是一个比较常见的经验值。在这个分辨率下普通屏幕上的文字、UI 控件、表格单元格基本能保持清晰同时视觉 token 的占用相对可控。当然如果你的图片包含大量极小字号文本可以试试把 tile_size 调到 960 或 1024如果你识别的是大块物体比如车辆、建筑512 也够用。实际项目中建议做一次小规模对比实验选择识别准确率和请求耗时都能接受的档位。3.2 切片重叠率的作用切片处理有个经典问题目标物体恰好落在切片边缘被一刀切成两半。左边切片只看到半个按钮右边切片也只看到半个按钮模型可能就认不出来。解决这个问题的方式是设置重叠率overlap。假设 tile_size 为 800重叠率为 10%那么每次向右移动 720 像素而不是 800 像素。这样相邻切片之间有 80 像素的公共区域落在边缘的目标会完整出现在至少一个切片内部。重叠率不是越大越好。重叠率太高切片数量会明显增加请求延迟和成本跟着上升重叠率太低又起不到保护作用。一般建议设置在 10% 到 20% 之间。3.3 缩略图与文本坐标的组合输入光有切片还不够。假设你给模型发了 20 张切片图但没有告诉它这些切片在原图中的位置模型无法建立空间关系。它可能识别出了“登录按钮”在切片 15 里但你不知道切片 15 对应原图的哪个区域。所以 vision-exp-tile 的输入由三部分组成全局缩略图让模型看到整张图的大致布局。切片图让模型看清局部细节。文本坐标标注给每个切片编号并说明它在原图的坐标范围。组装后的消息结构大概是用户消息 1. 一张缩略图。 2. 文本提示以下是该原图的切片图每个切片编号后的坐标为原图坐标范围。 3. 切片 0 图片对应原图坐标 (0, 0) 到 (800, 800)。 4. 切片 1 图片对应原图坐标 (720, 0) 到 (1520, 800)。 5. 切片 2 图片对应原图坐标 (0, 720) 到 (800, 1520)。 ... ...这样模型在识别切片时既能看到清晰的局部像素又能通过文本坐标理解“这块图位于原图左上角偏右的位置”最终输出结果也更稳定。3.4 坐标换算逻辑模型识别完切片后返回的坐标通常是“切片内坐标”。我们需要把它换算成“原图坐标”。换算公式非常简单原图横坐标 切片左上角横坐标 切片内相对横坐标 原图纵坐标 切片左上角纵坐标 切片内相对纵坐标举个例子。切片左上角在原图中的坐标是 (720, 0)模型在切片内识别到一个按钮中心点相对坐标是 (400, 300)那么按钮在原图中的中心点就是 (720 400, 0 300) (1120, 300)。这个换算逻辑虽然简单但在实际工程中非常关键。没有这一步模型识别出来的目标位置无法映射回原图后续的自动点击、框选标注、联动操作都无从谈起。4. 完整实战案例下面我们实现一个可以运行的 vision-exp-tile 完整示例。这个示例会读取一张本地大图自动切片生成缩略图组装视觉模型请求并把模型识别结果换算回原图坐标。4.1 创建项目和依赖在项目根目录下创建requirements.txtPillow9.1.0 openai1.0.0 requests2.31.0安装依赖pip install -r requirements.txt4.2 实现切片工具 vision_exp_tile.py创建vision_exp_tile.py写入以下代码# 文件路径vision_exp_tile.py import base64 from io import BytesIO from PIL import Image def split_image(img, tile_size800, overlap_ratio0.1): 将图片切成多个 tile_size 大小的切片返回切片信息列表。 每个切片信息包含 - index: 切片编号 - box: 切片在原图中的坐标范围 (x0, y0, x1, y1) - image: 切片 PIL.Image 对象 width, height img.size step max(1, int(tile_size * (1 - overlap_ratio))) tiles [] y 0 while y height: x 0 while x width: y0 y x0 x y1 min(y tile_size, height) x1 min(x tile_size, width) # 边缘对齐最后一块如果不足 tile_size回退起点保证贴到图片右下角 if y1 - y0 tile_size and y0 0: y0 max(0, y1 - tile_size) if x1 - x0 tile_size and x0 0: x0 max(0, x1 - tile_size) tile img.crop((x0, y0, x1, y1)) tiles.append({ index: len(tiles), box: (x0, y0, x1, y1), image: tile, }) if x1 width: break x step if y1 height: break y step return tiles def create_thumbnail(img, max_size512): 生成缩略图保持宽高比。 img img.copy() img.thumbnail((max_size, max_size), Image.Resampling.LANCZOS) return img def image_to_base64(img, formatJPEG, quality90): 将 PIL Image 转为 base64 字符串。 img img.convert(RGB) buffer BytesIO() img.save(buffer, formatformat, qualityquality) return base64.b64encode(buffer.getvalue()).decode(utf-8) def map_to_original(tile_box, x_rel, y_rel): 将切片内相对坐标换算为原图坐标。 x0, y0, _, _ tile_box return x0 x_rel, y0 y_rel这段代码有几个关键点split_image使用 while 循环而不是 for 循环因为需要手动控制切片步长并在边缘做回退处理。边缘回退保证了最后一块切片能覆盖到原图右下角不会出现“最后一行/列没有被切到”的问题。默认把图片转成 RGB 模式再编码成 JPEG避免 PNG 图片带透明通道导致接口报错。4.3 编写模型调用与请求组装创建call_model.py写入以下代码# 文件路径call_model.py import os from vision_exp_tile import ( create_thumbnail, image_to_base64, map_to_original, split_image, ) def build_messages(img, tile_size800, overlap_ratio0.1): 组装视觉模型消息。 thumbnail create_thumbnail(img, max_size512) thumbnail_b64 image_to_base64(thumbnail) tiles split_image(img, tile_sizetile_size, overlap_ratiooverlap_ratio) content [] content.append({ type: text, text: 这是一张原始图片的缩略图和多个局部切片图。 }) content.append({ type: image_url, image_url: { url: fdata:image/jpeg;base64,{thumbnail_b64} } }) content.append({ type: text, text: f原图被切成 {len(tiles)} 个 800x800 的切片每个切片编号后的坐标是切片左上角和右下角在原图中的坐标。请仔细识别切片内容回答时先给出切片编号和切片内坐标。 }) for tile in tiles: x0, y0, x1, y1 tile[box] tile_b64 image_to_base64(tile[image]) content.append({ type: text, text: f切片 {tile[index]}原图坐标范围 ({x0}, {y0}) 到 ({x1}, {y1}) }) content.append({ type: image_url, image_url: { url: fdata:image/jpeg;base64,{tile_b64} } }) return [{role: user, content: content}] def call_vision_model(messages, modelyour-vision-model): 调用视觉模型。 这里以 OpenAI 兼容接口为例实际使用时需要替换为你的模型名称和 API 配置。 from openai import OpenAI client OpenAI( api_keyos.environ.get(VISION_API_KEY, your-api-key), base_urlos.environ.get(VISION_API_BASE, https://api.openai.com/v1), ) response client.chat.completions.create( modelmodel, messagesmessages, temperature0.2, ) return response.choices[0].message.content def parse_and_map_result(model_output, tiles): 模拟解析模型输出。 这里展示坐标映射思路从模型输出中提取切片编号和切片内坐标然后换算成原图坐标。 实际项目中你可以用正则或 JSON 解析来提取结构化结果。 results [] for tile in tiles: tile_box tile[box] # 假设模型输出了切片内相对坐标 (rel_x, rel_y) rel_x 0 rel_y 0 orig_x, orig_y map_to_original(tile_box, rel_x, rel_y) results.append((orig_x, orig_y)) return results def main(): # 读取测试图片请替换为你自己的图片路径 input_path images/demo.png img __import__(PIL.Image, fromlist[Image]).open(input_path) messages build_messages(img) print(消息已组装完成切片数量, len(messages[0][content]) // 2) # 实际运行时取消下面注释 # result call_vision_model(messages) # print(模型输出, result) if __name__ __main__: main()这里的call_vision_model是核心调用入口。不同模型服务商的 SDK 调用方式可能有差异你只需要把client.chat.completions.create这部分替换成你实际使用的视觉模型接口即可。4.4 将切片图片保存到本地如果你想先看看切片效果可以在脚本中增加保存功能。在main函数中加入def main(): import os from PIL import Image input_path images/demo.png img Image.open(input_path) tiles split_image(img) os.makedirs(output/tiles, exist_okTrue) thumbnail create_thumbnail(img) thumbnail.save(output/thumbnail.jpg, quality90) for tile in tiles: tile[image].save( foutput/tiles/tile_{tile[index]}.jpg, quality90, ) print(f原图尺寸{img.size}) print(f切片数量{len(tiles)}) print(切片详情) for tile in tiles: print(tile[index], tile[box])运行这个脚本你会看到类似下面的输出原图尺寸(3200, 2400) 切片数量15 切片详情 0 (0, 0, 800, 800) 1 (720, 0, 1520, 800) 2 (1440, 0, 2240, 800) 3 (2160, 0, 2960, 800) 4 (2400, 0, 3200, 800) ...注意第 4 个切片原图宽度 3200按步长 720 计算最后一列的起点是 2400此时x1 min(2400 800, 3200) 3200切片宽度 800刚好覆盖到图右边缘。如果你的原图宽度不是 800 的整数倍边缘切片会自动回退保证不丢像素。4.5 运行与验证写一个完整的调用示例把识别逻辑串起来# 文件路径run_demo.py import json from PIL import Image from call_model import build_messages, call_vision_model from vision_exp_tile import split_image def main(): img Image.open(images/demo.png) messages build_messages(img) # 这里替换成你自己部署的视觉模型名称 model_name your-vision-model output call_vision_model(messages, modelmodel_name) print( 模型输出 ) print(output) # 后续可以继续解析 output提取识别到的目标框 # 建议让模型输出 JSON例如 # [{slice: 3, rel_box: [100, 150, 300, 280], label: 登录按钮}] # 然后通过 map_to_original 换算到原图坐标 if __name__ __main__: main()如果你想简化调试也可以先用本地图片不调用真实模型只验证切片逻辑是否正确再逐步接入视觉模型接口。这样能更快定位问题是在切片层还是在模型层。5. 常见问题与排查思路在实际使用 vision-exp-tile 的过程中最容易遇到下面几类问题我这里整理成了一张排查表。问题现象常见原因解决思路切片数量太多请求体超长原图分辨率过大tile_size 设置太小增大 tile_size 到 960 或 1024降低重叠率到 5%目标横跨两个切片识别被拆散目标恰好落在切片边缘提高 overlap_ratio 到 0.15~0.2或者先用目标检测模型定位再针对性切块模型返回坐标漂移定位不准模型没有理解“切片内坐标”的要求在 Prompt 中明确要求“以切片图片的左上角为原点输出相对坐标”限制模型输出 JSON 格式图片传给 API 后报格式错误原图带 alpha 通道或格式不支持统一调用 convert(RGB) 并保存为 JPEGbase64 字符串太长接口 413单张切片体积过大降低 quality 参数改用 WebP适当减小 tile_size模型上下文窗口溢出组装消息包含过多图片分批处理或用缩略图做粗筛后只对重点区域切片同一张图每次识别结果不一致部分模型带随机性降低 temperature使用确定性采样参数增加投票机制这里重点说一下模型上下文窗口溢出。很多视觉模型虽然支持图片输入但图片会折算成大量 token。一张 800×800 的 JPEG 图片经过 base64 编码后字符长度通常在 10 万级别折算成视觉 token 可能达到上千甚至更多。切片数量一多很容易超过上下文窗口。这种情况建议先看缩略图让模型先用文本描述哪些区域需要重点识别再只对指定区域做二次切片。这其实就是“粗粒度定位 细粒度识别”的两阶段方案。另一个高频坑是坐标问题。模型在回答时有时会基于缩略图坐标说位置这时如果你把它当成切片内坐标来换算结果会完全错误。为了避免歧义我建议在 Prompt 中强制要求模型输出 JSON并且字段中同时包含slice_index和rel_box这样程序侧可以明确知道使用的是哪个切片的相对坐标再做统一换算。6. 最佳实践与工程建议把 vision-exp-tile 从“能跑”变成“好用”还有不少工程细节值得打磨。6.1 根据任务类型选择切片策略不是所有图都需要整张切片。如果任务是“找图中的所有按钮”可以先传缩略图让模型用文本列出大致的按钮分布区域再对相关区域做切片识别。如果任务是“识别整页报表的小字数字”那基本需要全量切片而且建议提高重叠率。如果任务是“判断图片里是否包含某个特定物体”单张缩略图往往就够了不需要切片。在实际业务里先判断信息密度和任务类型再决定是否切片能省下不少 API 调用成本。6.2 缓存切片结果切片本身是确定性操作只要原图不变切片就不会变。在服务端实现时可以把切片结果缓存下来比如把每个切片的 base64 编码按照原图 hash 存储。这样当多个客户端请求同一张图或者同一张图要多次调用模型时就不需要反复切图编码能显著降低 CPU 和内存开销。缓存建议使用独立的 Cache 服务比如 Redis 或者本地文件系统key 设计为原图hash_tileSize_overlap。6.3 控制请求体积图片压缩对成本影响很大。800×800 的 JPEGquality 90 和 quality 70 的体积差距可能超过一半而模型识别效果往往并不会因此明显变差。建议在批量场景下做一次质量测试找到视觉可接受的最低 quality 值。另外如果原图是 PNG 带透明通道切片后务必转成 RGB 再编码。不然透明区域会变成黑色或者白色干扰模型判断。6.4 并发与限流如果你要在一个流程里识别大量切片建议控制并发数量。很多模型 API 有 QPS 限制超过限制会返回 429。可以使用信号量或者线程池限制并发数比如同时最多 5 个请求。import threading semaphore threading.Semaphore(5) def limited_call(messages): with semaphore: return call_vision_model(messages)同时要设计重试机制对 429、超时、5xx 错误做指数退避重试避免瞬时流量打垮模型服务。6.5 安全与合规识别图片往往涉及业务敏感数据。涉及用户图片、内部文档时务必遵守最小权限原则不要随意把敏感图上传到第三方大模型服务优先考虑本地部署的开源视觉模型。如果必须使用云服务先做脱敏、打码、裁剪处理并确认数据存储和传输链路合规。所有模型调用日志中不要保存完整图片的 base64 编码只记录切片编号、坐标、模型返回的结构化结果即可。在涉及批量处理生产数据前先在测试环境用小样本验证效果做好备份和回滚方案。6.6 引入两阶段识别架构对于识别精度要求较高的项目建议把流程升级为两阶段粗筛阶段模型看缩略图输出候选区域的文本描述。精识别阶段根据候选区域坐标截取原图对应位置的切片再做一次细粒度识别。这样做的好处是切片数量从“全图均匀切”变成“按需切”请求量可能下降一个数量级同时精度也会提升因为模型注意力集中在真正有用的区域。7. 总结与下一步这篇文章围绕 vision-exp-tile 智能识图插件完整拆解了大图切片识别方案的核心原理和实战代码。通过 800×800 切片、重叠保护、全局缩略图、文本坐标标注可以让多模态大模型在一张大图上同时获得“全局视野”和“局部细节”从而解决大图识别不清、小字丢失、目标定位不准等问题。整个方案的核心本质是分而治之把大图切割成模型能够高质量处理的小块再用坐标体系把这些小块“拼回去”。无论你是做 AI 自动化测试、文档解析、OCR 增强还是视觉问答应用这套思路都可以直接复用到自己的项目里。如果你继续往深走可以考虑这几个方向把切片与目标检测模型结合用检测框动态决定切片位置而不是均匀网格切图。在 OCR 场景下先做文本行检测再对包含文本的行切片识别进一步降低无效计算。将这套切片逻辑封装成 FastAPI 微服务作为视觉识别的公共预处理层供多个业务线复用。如果你在落地过程中遇到切片参数调优、坐标换算、模型上下文超限的问题欢迎收藏这篇文章按上面的排查表一步步对照处理。实践出真知拿一张自己的大图跑一遍比看十遍理论都更有帮助。
返回列表