☰
计算机视觉算法实战:候选框生成、词袋模型与语言模型辅助识别
2026/10/5 2:42:22 网站建设 项目流程

简介:计算机视觉算法实战型PDF资料,面向系统学习目标检测与行为识别技术的开发者、研究生及课程备课教师。内容围绕三条核心技术线展开:一是选择性搜索用于物体定位,介绍层次分组、不同颜色空间与区域相似度函数,并给出帕斯卡重叠评分等实验指标;二是视觉范围度量,通过字袋分类证据反向投影,分析刚性对象与非刚性对象在空间范围上的差异;三是基于语言模型的动作/事件识别,对比LDA-R与TypeDM两种模型在Pascal VOC2007数据集上的识别效果与适用场景。资源为单份PDF,压缩包仅482KB,轻量便携,便于在通勤或碎片时间阅读,也适合打印后对照算法公式与实验结论做笔记。已有527人学习浏览,内容聚焦经典算法实际落地路径,可作为算法选型参考、论文复现导读和计算机视觉课程辅助阅读资料。

1. 计算机视觉算法实战:候选框、词袋与语言模型三条线的拆解

做计算机视觉项目实战的人,现在多数是直接调 YOLO、跑现成检测库,很少有人再回头啃区域提议和词袋这类经典算法。我拆完这份 PDF 反而想把它翻出来:它对应的是计算机视觉算法实战里最容易被跳过的一段——选择性搜索(Selective Search)、词袋模型、语言模型辅助动作识别。这三个项目不涉及多深的神经网络,却把“候选框怎么生成”“分类证据到底落在图像哪里”“视觉样本不够时怎么借语言”讲得很透。拿选择性搜索来说,它在 4 秒内生成 2134 个候选框时,平均最佳 Pascal 重叠(Average Best Pascal Overlap,ABO)就能到 0.804,这个数字放到今天做 RPN 依然有参考意义。新手拿它补原理,熟手拿它调参数、做评估,都不会觉得浪费。

2. 选择性搜索:把滑窗换成候选框,参数与 ABO 评估落地

2.1 为什么滑窗不划算

早期目标检测最常见做法是滑动窗口:固定窗口大小和步长,在整幅图上穷举位置,然后对每个窗口做分类。问题在于尺度。一个勺子可以出现在沙拉碗里,沙拉碗又在餐桌上,图像天然是层次性的。穷举窗口要么设多种尺度,要么把步长压到很小,计算量立刻失控。

选择性搜索的思路是“数据驱动”:先用图像分割把像素分组,再通过层次合并得到一组“有可能包含物体”的区域,而不是在像素级穷举。这样既保留了图像本身的结构线索,又不至于把每个位置都试一遍。PDF 里那句“图像本质上是层次性的,需要所有尺度才能找到物体”,其实就是选择性搜索存在的全部理由。

2.2 层次分组:颜色、纹理、大小、填充四类相似度

选择性搜索的完整流程分三步:先生成初始区域,再计算相邻区域相似度,最后按相似度从高到低迭代合并。合并时并不是单一指标,而是四类相似度的加权组合。

  • 颜色相似度:对每个区域做颜色直方图,用直方图交集衡量接近程度。为了照顾不同光照和设备差异,代码里通常会在多个颜色空间分别计算。
  • 纹理相似度:用局部二值模式或 SIFT 描述子的直方图衡量,纹理一致的区域更容易合并。
  • 大小相似度:鼓励小区域先合并,避免某个大区域吞掉周围所有碎片。优先让小区域合并,目的是让分割结果在各个尺度上都有机会出现。
  • 填充相似度:衡量两个区域合并后能否“填满”它们的包围盒。如果两个区域拼起来几乎成为一个完整矩形,说明它们很可能是同一个对象的一部分。

实际实现里,四个相似度各占 0.25 是常见起点。这个权重不是写死的,PDF 中“使用不同的分组策略,并且各不相同”就是在说:为了覆盖不同图像条件,需要让分组策略有足够多样性,而不是调一组完美参数走天下。

2.3 一个可直接跑的 selectivesearch 样例

复现选择性搜索不需要从头写层次合并,Python 生态里有现成实现。我一般这样用:

import cv2 import selectivesearch # 读取图像并转 LAB 颜色空间 img = cv2.imread("table.jpg") img_lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) # 生成候选区域 # scale: 控制分割精细度,越大越容易生成大区域 # sigma: 高斯平滑系数,越大区域边界越平滑 # min_size: 最小区域像素数,过滤碎片 candidate_regions, _ = selectivesearch.selective_search( img_lab, scale=500, sigma=0.9, min_size=10 ) # 提取矩形框并去重 boxes = [] for region in candidate_regions: rect = region["rect"] if rect not in boxes: boxes.append(rect) # 过滤过小的框 min_area = 50 * 50 boxes = [b for b in boxes if b[2] * b[3] >= min_area] print("候选框数量:", len(boxes))

逻辑说明:candidate_regions返回的是区域字典列表,每个区域里有rect、labels、size等字段。rect的格式是(x, y, w, h),注意不是(x1, y1, x2, y2)。去重是必要的,选择性搜索的多样化策略会产生大量重叠框,完全相同的框直接丢弃能省不少后续分类时间。

参数说明:scale=500是经验值,图像分辨率高时可以上调到 800 甚至 1000。sigma控制分割的平滑程度,太小区域边界碎,太大容易把小目标并掉。min_size=10指初始分割阶段小于 10 像素的区域会被合并,这个值建议按数据集最小目标面积调整,而不是固定不变。

2.4 用 ABO 评估候选框质量

候选框生成完,下一步是回答“这些框到底好不好”。PDF 里给的关键指标是 ABO:平均最佳 Pascal 重叠。它的计算逻辑是:对每个真实目标框,从所有候选框里找到与它重叠度最高的那个,计算 Pascal 重叠分数,最后对所有目标求平均。

def pascal_overlap(box_a, box_b): # box: (x, y, w, h) x1 = max(box_a[0], box_b[0]) y1 = max(box_a[1], box_b[1]) x2 = min(box_a[0] + box_a[2], box_b[0] + box_b[2]) y2 = min(box_a[1] + box_a[3], box_b[1] + box_b[3]) inter = max(0, x2 - x1) * max(0, y2 - y1) area_a = box_a[2] * box_a[3] area_b = box_b[2] * box_b[3] union = area_a + area_b - inter return inter / union if union > 0 else 0 def average_best_overlap(proposals, gt_boxes): scores = [] for gt in gt_boxes: best = max(pascal_overlap(p, gt) for p in proposals) scores.append(best) return sum(scores) / len(scores)

逻辑说明:这里的proposals就是上一步生成的候选框列表,gt_boxes是标注框列表。注意是“每个 GT 框挑一个最高重叠”,而不是“每个候选框挑一个最高分数取平均”。两者含义差距很大:前者衡量召回上限,后者容易被大量重复框灌水。论文里 ABO 0.804 的含义是:平均下来,每个真实目标至少有一个候选框与它达到约 80% 的重叠。这个数值意味着后续用词袋模型做分类时,定位误差不会成为主要瓶颈。

提示:如果 ABO 达不到预期,优先检查min_size是否把目标整体切碎,其次检查scale是否太大导致小目标被合并掉。跟分类精度无关,先排查候选框。

3. 词袋模型不是黑匣子:分类证据反投影与视觉范围

3.1 从 SIFT 到视觉单词

词袋模型(Bag of Words)在图像领域的经典流程是:提取局部特征、聚类成视觉单词、统计直方图、交给分类器。流程不复杂,但它长期被当成黑匣子用——大家只知道输入图像输出类别,却不知道分类器到底在看图像的哪一块。

PDF 里针对猫那张热图说明了一个现象:黄色区域代表强阳性证据,蓝色代表强阴性证据,灰色是中性。如果只用特征直方图做分类,你根本说不清这些证据来自物体本身还是背景。正因如此,作者才专门做了一个“把分类证据反向投影回图像”的项目。

先看词袋模型的训练基础部分:

import cv2 import numpy as np from sklearn.cluster import KMeans # 提取所有训练图像的 SIFT 描述子 sift = cv2.SIFT_create() descriptors = [] for img_path in train_images: gray = cv2.cvtColor(cv2.imread(img_path), cv2.COLOR_BGR2GRAY) kps, descs = sift.detectAndCompute(gray, None) if descs is not None: descriptors.append(descs) all_descs = np.vstack(descriptors) # 聚类成视觉单词 K = 200 kmeans = KMeans(n_clusters=K, random_state=0).fit(all_descs)

逻辑说明:SIFT 描述子对亮度、旋转有一定不变性,适合做视觉单词的原料。KMeans聚成 200 个簇后,每个特征点会被分配一个 0 到 199 的整数 ID,这个 ID 就是视觉单词。K 的取值没有标准答案:类别少、图像简单时 100 够用;类别多、场景杂时往 400 上调。

参数说明:random_state=0是为了可复现。SIFT 描述子是浮点向量,直接喂 KMeans 没问题。如果换 ORB 或 BRIEF,特征是二进制向量,需要换聚类方式或者先转成浮点,否则距离计算会出问题。

3.2 把 SVM 权重反投影成热图

训练好分类器之后,关键是反投影。每个视觉单词在 SVM 里都有一个权重,正权重表示“这个单词出现会增加目标类别的得分”,负权重表示“它出现更倾向于背景”。反投影就是把每个特征点携带的权重放回它原来的像素位置。

# 假设 svm_coef 是训练好的线性 SVM 每个视觉单词的权重 # kps 是测试图像的关键点,descs 是对应描述子 heat = np.zeros_like(gray, dtype=np.float32) for pt, desc in zip(kps, descs): word_id = kmeans.predict(desc.reshape(1, -1))[0] x, y = int(pt.pt[0]), int(pt.pt[1]) heat[y, x] += svm_coef[word_id] # 高斯模糊让热图平滑 heat = cv2.GaussianBlur(heat, (0, 0), sigmaX=2.0)

逻辑说明:heat是一个与图像同尺寸的浮点矩阵,每个像素累加它所在位置的视觉单词权重。最后用高斯模糊做平滑,消除特征点稀疏带来的噪点。把heat用伪彩色叠加到原图上,就能看到类似 PDF 里猫那张图的“强阳性证据 / 强阴性证据”分布。

这里有个容易误读的地方:热图不等于“注意力图”。它反映的是当前分类器对图像各区域的依赖程度,而不是“目标应该在哪里”。如果一个分类器大量依赖背景纹理,热图照样会指向背景,而且分类分数可能还挺高。这就是词袋模型的“黑匣子”所在。

3.3 场景上下文和对象边界的作用

PDF 在这一节做了两个方向的实验,结论都非常工程化。第一个方向是不给目标位置,看分类证据来自图像哪些位置。结果发现:环境(上下文)对分类有显著贡献,尤其是船这一类,物体区域本身的贡献甚至是负的。也就是说,分类器很大程度上是靠“周围是水面、天空”来判断“这里有船”,而不是靠船体本身。

第二个方向是假设存在一个理想包围盒,分别评估物体内部、边界、环绕区域的贡献。结论是:有了理想定位之后,环境的作用基本消失。“良好的目标定位能带来相当大的精度提升”这句话,翻译成工程语言就是:先保证候选框质量,比花大力气优化分类器更划算。

基于这些实验,PDF 给出一个很实用的判断规则:视觉范围由对象的类别决定。刚体对象(比如瓶子、汽车)的视觉范围就是对象本身;非刚性对象(比如人、动物)的空间范围是无限的,也就是远处上下文也能帮上忙;按功能分类的对象(比如“交通工具”这类抽象类别)干脆把整张图作为空间范围。

这对项目实战的直接启示是:做目标检测数据增强时,不要无脑对全图裁剪。刚体类可以放心裁到对象周边,非刚性类和功能类保留更多上下文,效果往往更好。这一点我在后面裁剪策略里还会提到。

4. 动作与事件识别:语言模型补足视觉样本的组合方法

4.1 为什么动作和事件很难做全监督

动作识别和事件识别有一个天然麻烦:类别数量近乎无限。一个物体可以搭配大量动词,比如“喂马”“骑马”“牵马”“拍马”,每个动作又可以有修饰语,印度婚礼和欧洲婚礼在视觉上完全不同。传统视觉识别要求每个类别都有大量人工标注样本,这种标注成本在动作识别场景下根本撑不住。

PDF 里提出的思路是换一个角度:不直接识别动作,而是先识别动作的组件。组件是物体,动作是通过物体和动词的组合表达出来的。视觉模型负责“看到对象并定位”,语言模型负责“这个对象通常会被怎样操作”。两部分组合起来,就能识别没有标注过的动作组合。

4.2 对象定位加语言模型的组合方式

组合方案分两层。底层是对象定位,PDF 用的是基于选择性搜索的字袋模型,也对比了 Felzenszwalb 的基于部分模型,结果字袋模型表现更好。上层是语言模型,用来给每个对象预测合理动作。作者对比了 LDA-R 和 TypeDM 两种语言模型,结论是 TypeDM 更优。

这种组合的优势在“不可见事件”上体现得很明显:视觉模型只需要知道“画面里有一只碗”,语言模型就能给出“盛饭”“清洗”“端起来”这类合理动作。即使没有任何“洗碗”的视觉训练样本,系统也能给出一个不算离谱的预测。对项目实战来说,这就是在标注预算有限时,用文本知识弥补视觉样本短缺的可行路径。

4.3 用 VOC2007 做一份对象—动作关联

PDF 里为 Pascal VOC2007 创建了人类行为标注,对象类别限制在 20 类,单个对象的动作频率是无偏的——这一点和大多数动作识别数据集不同,后者通常追求每个类别样本量均衡,结果反而扭曲了真实场景中的动作分布。

下面这段代码是“对象—动词”统计的最小实现,能帮你快速理解语言模型那部分在做什么:

from collections import defaultdict, Counter docs = [ "ride a horse", "feed a horse", "walk a dog", "wash a car", "drive a car", ] # 统计每个对象对应哪些动词 obj_verb = defaultdict(Counter) for doc in docs: verb, obj = doc.split(maxsplit=1) obj_verb[obj][verb] += 1 # 转成条件概率:对象出现时,各动词的概率 for obj, verb_counter in obj_verb.items(): total = sum(verb_counter.values()) probs = {v: c / total for v, c in verb_counter.items()} print(obj, probs)

逻辑说明:真实的语言模型比这段代码复杂得多,LDA 这类主题模型会把语料中的“对象—动词”关系放到主题维度上做平滑,TypeDM 则更进一步对条件分布建模。但核心逻辑就是这段代码做的事:从文本知识中统计“某个对象更可能被施加哪些动作”,再把统计结果接到视觉对象检测器后面。

工程上的注意点:对象—动词关联的语料来源决定了模型上限。拿通用语料统计出来的“horse”关联动词可能包含“race”“bet on”,这些动作在 VOC 数据集里根本没有对应视觉样本。所以 PDF 特意强调数据是有偏设计:限定在 20 个对象类别内,但对每个对象发生的动作频率不做人为均衡。这样做的好处是评价结果更接近真实使用场景,代价是类别不平衡问题要由模型自己扛。

提示:如果动作类别里包含大量低频组合,先不要上复杂深度模型。用“对象检测器 + 条件概率表”跑一遍基线,通常就能看出问题到底在视觉端还是文本端。

5. 高频坑位排查:候选框、词袋与语言模型的五个翻车点

5.1 候选框与数据准备阶段的排查

现象一:选择性搜索跑得很慢,一张 1080p 图像耗时十几秒,完全达不到论文说的“4 秒 2134 个框”。

原因:scale和sigma配得不合适。scale太小会让初始分割区域非常碎,后续合并步骤暴增;sigma太大则让区域边界过度平滑,合并顺序混乱。另外,论文那组数字基于 VOC 数据集的小图,不是 4K 大图。

解决:先把图像短边缩到 400 到 600 像素,用scale=500, sigma=0.9, min_size=10跑通流程,再逐步提分辨率。如果每张图候选框超过 5000 个,先查min_size是不是设得太小,而不是直接怀疑库有问题。

现象二:ABO 分数一直上不去,换了几组参数都卡在 0.5 左右。

原因:min_size太大导致小目标被合并进背景区域;或者用多边形标注框直接和矩形候选框算 IoU,结果被“空白角”拉低。VOC 这类检测数据集的 GT 本来就是矩形框,但其他数据源不一定。

解决:先对每个 GT 框做面积统计,按小目标面积反推min_size。如果标注是多边形,先把标注转成外接矩形再算 ABO,否则数值会系统性偏低。

现象三:候选框数量合适,但大量框重叠在同一个对象上,其他对象一个框都没有。

原因:层次分组策略不够多样,只用了单一颜色空间,导致某些对象的区域合并路径完全一致,生成的框全挤在纹理最丰富的区域。

解决:换颜色空间或合并多种颜色空间的结果。RGB、HSV、Lab 各跑一遍,把候选框合在一起。选择性搜索的“多样化”不是可选项,是保证召回的基本手段。

5.2 建模与评估阶段的排查

现象四:词袋反投影热图集中在背景上,物体内部几乎没证据。

原因:这大概率不是 bug,而是分类器真的在靠上下文分类。SIFT 特征在纹理丰富的背景区域更密集,背景视觉单词在 SVM 里获得了更高权重。PDF 里“船的对象区域贡献为负”就是这个现象的极端案例。

解决:先不要急着调分类器,用理想包围盒把图像裁出来重新看热图。如果裁掉背景后分数大幅下降,说明分类器依赖上下文,这在纯目标分类项目里是可以接受的;如果业务方要求热图集中在物体上,就得给分类器加边框约束或改用区域特征。

现象五:动作识别模型的效果跟“对象—动词”先验表差不多,视觉部分好像没有贡献。

原因:对象检测器准确率太低,传到语言模型的“对象框”大部分是背景,语言模型只能退回到先验概率做预测。问题不在语言模型,在视觉端。

解决:单独评估对象定位的 ABO 或检测精度。如果候选框质量差,先回来调选择性搜索的scale和min_size,不要在一个不可靠的视觉结果上叠加复杂的语言模型。

6. 进阶验证:先跑 ABO 再谈 mAP,把视觉范围结论用到裁剪策略

把这份 PDF 的项目拆完,最值得带走的是两个习惯:第一个是“先验候选框,再谈分类”;第二个是“类别决定视觉范围”。这两个结论放到现代检测流程里依然能用。

我现在接到一个新检测任务,第一步不是训练网络,而是先跑一轮候选框 ABO 评估。对每个类别单独计算 ABO,按类别排序,看哪些类目的召回上限特别低。类别间 ABO 差距很大时,通常是min_size和scale照顾了大目标、丢了小目标,或者颜色空间策略对某些类不敏感。这一步能避免后面整个训练过程都在白费力气。候选框质量决定召回上限,分类器再强也救不回没被生成的框。

第二个习惯和裁剪策略有关。数据预处理阶段做随机裁剪时,我会先给图像分类别:刚体类按对象包围盒外扩一点做裁剪;非刚性类和按功能定义的类保留更大范围的上下文。PDF 的实验已经说明,非刚性对象的视觉范围是无限的,所有尺度都能产生同样好的结果;按功能分类的对象甚至会把整张图作为空间范围。如果对这些类别也做激进裁剪,等于主动砍掉分类器赖以判断的上下文信息。

从工程角度看,这套旧算法项目最妙的还不是单点效果,而是“先证明问题存在,再设计对应方法”的推进方式。选择性搜索证明候选框可以用多样化分组快速生成,词袋反投影又揭示了分类器对上下文的隐性依赖,语言模型则提供了视觉样本不足时的补充通道。三件事连起来,正好是一个检测系统从候选框、特征到知识的完整闭环。

以后我每次拿新数据集做检测,都会强制先走一遍 ABO 评估,再看 mAP,最后才敢说模型效果好坏。这个顺序帮我过滤了不少玄学,也希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询