-
-
[原创]腾讯验证码识别之“旋转最快的物体”
-
发表于: 2026-9-10 13:12 375
-
动态类验证码除了“点击运动最慢的元素”外,还有一类反向题型:画面中多个图形各自旋转,要求点击旋转最快的。度量对象从平动速度变为转动角速度,但整体分析框架一致:目标检测、跨帧关联、运动估计、速度比较。本文基于 Python + OpenCV 给出实现分析,重点说明角速度估计与多信号融合打分的方法构成。

一、问题定义
输入:一段 WebM 或 MP4 短视频,画面中存在若干旋转图形,图形可能同时带有轻微平动。输出:旋转最快图形的中心坐标 (x, y)。需要处理的难点:
相邻帧之间的旋转量通常很小,对角度估计的精度要求较高;
图形可能边旋转边平移,需要将平动分量从角速度估计中分离;
各图形旋转方向可能不同,速度比较应基于角速度绝对值;
输出坐标必须对应提交时刻的图形位置。
二、总体流程
视频下载 → 逐帧读取 → 图形检测 → 跨帧关联 → 逐帧旋转角速度估计
→ 轨迹特征汇总与归一化打分 → 最快目标输出
三、视频读取与性能设计
使用 cv2.VideoCapture 读取全部帧与帧率信息,帧数不足时直接判定失败。旋转速度估计依赖稠密光流,计算开销较大,因此采用两档运行模式:
快速档(默认):光流在降低分辨率的小图上计算,图形检测与坐标输出保持原始分辨率;
全精度档:全部环节在原始分辨率上进行,作为精度敏感场景的兜底。
四、图形检测
对每一帧进行图形检测,处理链路:
灰度化 → 自适应阈值二值化 → 形态学闭运算与开运算 → 轮廓提取 → 几何过滤 → 质心计算
自适应阈值适合局部光照不均匀的画面;形态学闭运算补全图形内部空洞,开运算清除孤立噪点。轮廓过滤包含两类条件:
面积区间:过小视为噪点,过大视为背景误检;
宽高比区间:过滤细长形干扰。
通过过滤的轮廓以图像矩计算质心,作为图形中心坐标。
五、跨帧关联
相邻帧之间需要确定“这一帧的哪个图形是上一帧的哪个”,关联策略:
以中心点距离构造代价矩阵;
叠加面积变化比例约束,超出允许范围的配对置为不可达;
全局最优一对一匹配(线性分配),无第三方依赖时退化为贪心匹配兜底。
同时约束中心距离与面积比例,是为了在图形交错、短暂遮挡时保持轨迹身份稳定,避免两个图形互相串号。
六、旋转运动估计
本节是方法的核心,采用两条互补的测角途径。
6.1 光流角速度
在图形掩码区域内计算稠密光流,利用刚体旋转的运动学关系估计角速度:
ω = (vx·ry − vy·rx) / r²
其中 (vx, vy) 为该像素的光流速度,r 为像素到图形质心的距离,ω 即绕质心的角速度。对区域内全部像素的角速度取中位数,可抵抗局部光流噪声。该途径直接度量“转得多快”,物理意义明确;为控制计算量,光流在低分辨率图上计算,图形掩码同步缩放。
6.2 姿态角位移
对图形轮廓求最小外接矩形,取其方向角,逐帧做差分得到姿态变化量,并进行 ±90° 环绕归一:
d = angle(cnt2) − angle(cnt1)
d = wrap(d, ±90°)
该途径对纹理简单、角点稀疏的图形更稳定,不依赖图像特征点。两条途径口径互补:光流负责测“速度”,姿态角负责测“变化量”,交叉验证、互为兜底。
七、特征汇总与打分
每条轨迹在跟踪过程中积累两类序列:角速度序列与姿态角位移序列。
7.1 特征聚合
对两类序列分别取高位统计量作为该轨迹的转速代表值:
角速度序列取高百分位值,代表该图形稳定达到的转速水平;
姿态角位移序列取高分段均值,避免个别帧的异常值主导。
取高位统计量而非均值,是因为“最快”判定应对应旋转强度,而非被慢速帧拉低的平均水平。
7.2 归一化与融合
两条特征在全体轨迹之间做 min-max 归一化后加权融合:
score = w1 · flow_score + w2 · shape_score
光流角速度直接对应题目语义,权重更高;姿态角位移作为辅助信号。得分最高的轨迹即旋转最快图形,返回其末位置坐标。画面中仅有一个目标时直接返回。
八、核心代码
以下为框架级伪代码,具体参数与细节不展开。
8.1 图形检测
for frame in frames:
thresh = adaptive_threshold(gray(frame))
thresh = morphology(thresh)
闭运算 → 开运算
for cnt in find_contours(thresh):
if not (min_area <= area(cnt) <= max_area):
continue
if not (min_ratio <= w_h_ratio(cnt) <= max_ratio):
continue
cx, cy = centroid(cnt)
图像矩质心
shapes.append({'center': (cx, cy), 'cnt': cnt, 'area': area(cnt)})
8.2 跨帧关联
cost = distance_matrix(prev_centers, curr_centers)
cost[dist > max_dist] = INF
cost[area_ratio_out_of_range] = INF
matches = linear_sum_assignment(cost)
无依赖时贪心兜底
8.3 角速度估计
光流角速度:刚体旋转切向速度公式
omega = (flow_y, flow_x 与半径向量的叉积) / r²
w = abs(median(omega))
姿态角位移:最小外接矩形方向角差分(±90° 环绕)
d = min_area_rect_angle(cnt2) - min_area_rect_angle(cnt1)
d = wrap_angle_90(d)
8.4 打分与输出
flow_score = normalize(high_percentile(track.w_flow))
shape_score = normalize(top_k_mean(track.d_shape))
score = w1 * flow_score + w2 * shape_score
fastest = argmax(score)
return tracks[fastest].last_pos
8.5 对外接口
def get_fastest_point(url: str):
video_path = download_video(url)
try:
frames, durations = read_video_frames(video_path)
return track_rotation(frames, durations)
finally:
remove(video_path)
九、工程要点
临时视频文件通过 try/finally 兜底清理,识别失败也不残留;
识别失败返回空值,由上层决定重试或放弃,不输出无效坐标;
识别模块只暴露单一接口(视频 URL → 坐标),与验证码整体流程解耦;
第三方依赖(线性分配库)缺失时自动降级,保证运行环境兼容。
十、方法特点与局限
方法特点:
无需训练样本,不依赖深度学习模型;
低分辨率光流显著降低计算开销,可毫秒级完成;
角速度与姿态角双信号互补,对纹理简单图形依然可用;
处理环节独立,可逐环节调试与可视化验证。
局限性:图形大幅平移、严重遮挡或纹理过少时,角度估计精度会下降;需要更高精度时可引入特征点跟踪或旋转回归类模型。
结语
该方法将“谁转得快”转化为“每帧角位移的统计”,由光流负责测速度、姿态角负责测变化量,两条信号归一化融合后排序输出。最终坐标具有明确的计算来源与时序语义。
公众号:JavaScript逆向笔记
来源:994K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6E0M7q4)9J5k6i4N6W2K9i4S2A6L8W2)9J5k6i4q4I4i4K6u0W2j5$3!0E0i4K6u0r3M7#2)9J5c8W2y4Y4N6i4A6^5K9g2y4J5y4$3u0$3z5g2u0Z5M7o6g2d9e0#2)9#2k6X3N6&6N6H3`.`.