资讯动态

智能车摄像头图像处理:五种阈值算法选型与代码实现

发布时间:2026/10/15 16:26:22 来源:尧图企业网站定制
简介这份资源面向参加智能车竞赛的选手及图像处理初学者聚焦摄像头图像处理中的阈值分割环节提供五种经典算法的C语言实现。包内共5个文件均为.c源码压缩包约3KB分别对应百分比阈值法、最佳迭代法、双峰法、灰度平均值法和最大类间方差法大津法每份代码可直接嵌入单片机工程便于对比不同阈值策略在赛道识别中的表现。百分比阈值法计算简单但对光照敏感最佳迭代法对噪声鲁棒双峰法适合对比度明显的图像灰度平均值法速度快却难应对复杂背景大津法则通过最大化类间方差适应噪声与光照变化。读者可借此快速搭建算法验证框架理解各方法的原理差异与适用边界为智能车视觉方案选型提供参考。目前已有316人学习下载。1. 五种阈值算法在智能车摄像头图像处理里的真实分工智能车竞赛的摄像头组别里图像处理的第一道分水岭往往不是边缘检测也不是特征匹配而是二值化。赛道是白底黑线还是黑底白线光照是均匀还是半边阴影阈值取 80 还是 120直接决定后续巡线是稳如老狗还是全程抽搐。百分比阈值、最佳迭代法、双峰法、灰度平均值法、最大类间方差法大津法这五种算法本质上都在回答同一个问题把 8 位灰度图切成黑白两片的那把刀到底该落在哪个灰度值上。新手常犯的错是随便挑一个固定阈值跑通就完事结果换个场地、换个时间段车直接冲出赛道。熟手则清楚这五种方法没有绝对优劣只有场景匹配度——有的吃光照均匀有的吃双峰明显有的计算量小到可以在中断里跑。下面按实际落地顺序把选型理由、代码实现、参数调节和踩坑记录一层层拆开。2. 五种阈值算法的原理差异与选型依据2.1 百分比阈值最粗暴但最可控的兜底方案百分比阈值不分析图像内容直接按灰度直方图的累积分布取分位点。比如设定 30%就是把灰度从 0 开始累积到总像素数的 30% 时对应的灰度值作为阈值。它的逻辑是赛道黑线占整幅图像的比例大致固定那么按比例切分就不会被局部高光或阴影带偏。在智能车场景里摄像头分辨率常见 80×60 到 160×120黑线宽度在图像中占比大约 10% 到 25%。如果赛道元素简单、光照稳定百分比阈值可以做到零计算开销——只需要在初始化时算一次直方图累积之后每帧直接查表。但它的致命伤也在这里一旦赛道上有大面积黑色区域比如起跑线、斑马线、车库入口累积分布会突变阈值直接跑飞。我一般把百分比阈值当作冷启动或故障降级方案。当其他算法因为图像退化而输出异常值时切回百分比阈值至少能保证车不立刻冲出赛道给调试留出反应时间。2.2 最佳迭代法用逼近思想找局部稳定点最佳迭代法的核心是先猜一个阈值把图像分成前景和背景分别算两边的平均灰度再取这两个平均值的中间值作为新阈值反复迭代直到阈值变化小于某个容差。它假设前景和背景的灰度分布是分开的且各自内部比较均匀。在智能车图像里这个假设在光照均匀时基本成立。迭代法通常 3 到 5 次就能收敛计算量比大津法小效果比固定阈值稳。但它对初始阈值敏感如果初始值落在背景区域内部可能收敛到错误的分割点。常见做法是用图像平均灰度作为初始值或者用上一帧的阈值热启动。迭代法的停止条件一般设 |T_new - T_old| 1 或迭代次数上限 10 次。在 80×60 的图像上单次迭代遍历全图约 4800 像素5 次迭代也就 24000 次访问在 100MHz 级别的单片机上完全跟得上 50fps 的节奏。2.3 双峰法直方图有峰才敢用双峰法建立在灰度直方图呈现明显双峰分布的假设上——前景一个峰背景一个峰两峰之间的谷底就是最佳阈值。智能车赛道在理想情况下黑线像素集中在低灰度区白色赛道集中在高灰度区直方图确实会出现两个峰。但实际比赛中光照不均匀、镜头暗角、赛道反光都会让直方图变成单峰或乱峰。这时候双峰法找谷底就会翻车可能把阈值定在背景内部导致整幅图全白或全黑。所以双峰法必须配合峰谷有效性判断如果两个峰的间距小于某个阈值或者谷底不够深就放弃双峰法切换到其他算法。双峰法的计算量主要在直方图统计和平滑。为了抗噪声直方图通常要做 3 到 5 点滑动平均否则谷底会被随机噪声干扰。在 256 级灰度下统计直方图需要遍历全图一次后续找峰找谷都是 256 次以内的操作整体开销可控。2.4 灰度平均值法快但容易被极端值带偏灰度平均值法直接把全图灰度均值作为阈值。它的计算量最小一次遍历就能同时得到均值和直方图。在赛道黑白比例接近 1:1 且光照均匀时均值确实接近最佳分割点。但智能车图像里白色赛道通常占 70% 以上黑色引导线只占小部分。这时候全图均值会偏向白色区域阈值偏高导致黑线被切掉一部分细线直接消失。改进做法是只统计图像中间区域ROI的均值或者对均值做偏移补偿比如阈值 均值 × 0.8。灰度平均值法适合作为其他算法的预处理或对比基准。我通常会在调试阶段同时输出均值阈值和大津法阈值观察两者的差值。如果差值超过 30说明图像分布严重偏斜需要检查曝光或赛道元素。2.5 最大类间方差法大津法计算量最大但最稳大津法遍历所有可能的阈值计算前景和背景的类间方差取使类间方差最大的阈值。它的理论依据是最佳阈值应该让两类的分离度最大。在智能车场景里大津法对光照变化和赛道元素变化的鲁棒性明显优于前四种代价是计算量最大。标准大津法需要先统计直方图然后遍历 256 个灰度级每个灰度级计算一次类间方差。在 80×60 图像上直方图统计 4800 次访问后续 256 次循环总共约 5000 次操作。如果每帧都跑在 50fps 下就是 25 万次操作/秒对普通单片机有压力。常见优化是隔帧计算阈值或者只在图像变化剧烈时重新计算。大津法在赛道图像上的表现当黑线清晰、光照均匀时阈值通常落在 100 到 140 之间当出现阴影时阈值会自动下移保证黑线不被误切。但大津法也有失效场景——如果图像中黑线占比极小比如远处弯道直方图接近单峰大津法会把阈值定在背景内部导致全白。2.6 五种算法的选型对照算法计算量抗光照变化抗赛道元素变化适用场景百分比阈值极低差差冷启动、降级兜底最佳迭代法低中中光照均匀、赛道简单双峰法中中差直方图双峰明显灰度平均值法极低差差快速预览、对比基准大津法高好好主算法、复杂光照选型建议主算法用大津法配合隔帧计算和上一帧阈值热启动双峰法作为大津法的验证分支当大津法输出异常时用双峰法交叉检查百分比阈值和灰度平均值法作为降级方案在图像严重退化时保证基本巡线能力。3. 在单片机上跑通五种算法的代码实现与参数调节3.1 图像数据结构与直方图统计在写阈值算法之前先定义好图像缓冲区和直方图数组。智能车摄像头常见输出格式是灰度图每像素 8 位分辨率 80×60 或 160×120。直方图用 256 个 uint16 或 uint32 存储取决于像素总数。// 图像缓冲区定义以 80x60 为例 #define IMG_W 80 #define IMG_H 60 #define IMG_SIZE (IMG_W * IMG_H) uint8_t img_buf[IMG_SIZE]; // 灰度图像 uint16_t hist[256]; // 直方图最大像素数 4800uint16 足够 // 统计直方图同时返回图像均值 uint8_t calc_histogram(uint8_t *img, uint16_t *hist, uint16_t size) { uint32_t sum 0; for (int i 0; i 256; i) hist[i] 0; for (int i 0; i size; i) { hist[img[i]]; sum img[i]; } return (uint8_t)(sum / size); // 返回均值供灰度平均值法使用 }这段代码一次遍历同时完成直方图统计和均值计算避免重复扫描图像。hist数组用uint16_t是因为 80×60 图像总像素 4800单个灰度级最多出现 4800 次uint16 上限 65535 足够。如果分辨率提升到 160×120总像素 19200仍然在 uint16 范围内。参数说明size传入实际像素数如果只处理 ROI 区域传入 ROI 的像素数即可。calc_histogram返回的均值可以直接用于灰度平均值法也可以作为迭代法的初始阈值。3.2 百分比阈值的查表实现百分比阈值不需要每帧重新计算只需要在初始化时根据目标百分比生成阈值之后每帧直接使用。但如果赛道元素变化剧烈可以每 10 帧重新计算一次。// 根据百分比计算阈值pct 为 0-100 uint8_t calc_pct_threshold(uint16_t *hist, uint16_t total, uint8_t pct) { uint32_t target (uint32_t)total * pct / 100; uint32_t accum 0; for (int i 0; i 256; i) { accum hist[i]; if (accum target) return (uint8_t)i; } return 128; // 兜底 } // 使用示例取 30% 分位点 uint8_t th_pct calc_pct_threshold(hist, IMG_SIZE, 30);逻辑说明target是目标累积像素数遍历直方图累积到超过target时返回当前灰度值。pct的选择需要根据赛道黑线占比调整。如果黑线在图像中占 15% 到 20%pct设 20 到 25 比较合适如果黑线较粗可以设 30。注意pct不是黑线占比而是累积分布的分位点通常比黑线占比略大因为黑线边缘有过渡像素。参数调节在调试阶段可以同时输出th_pct和大津法阈值观察两者差值。如果th_pct比大津法阈值高 20 以上说明pct设大了黑线会被切细如果低 20 以上说明pct设小了背景噪声会被误切为黑线。3.3 最佳迭代法的收敛控制迭代法的关键在于初始阈值和停止条件。初始阈值用图像均值停止条件用阈值变化小于 1 或迭代次数上限。// 最佳迭代法返回最终阈值 uint8_t calc_iter_threshold(uint8_t *img, uint16_t size, uint8_t init_th) { uint8_t th init_th; for (int iter 0; iter 10; iter) { uint32_t sum_fg 0, sum_bg 0; uint16_t cnt_fg 0, cnt_bg 0; for (int i 0; i size; i) { if (img[i] th) { sum_fg img[i]; cnt_fg; } else { sum_bg img[i]; cnt_bg; } } if (cnt_fg 0 || cnt_bg 0) break; // 防止除零 uint8_t th_new (uint8_t)((sum_fg / cnt_fg sum_bg / cnt_bg) / 2); if (th_new th) break; // 收敛 th th_new; } return th; }逻辑说明每次迭代遍历全图分别计算前景和背景的平均灰度取两者中间值作为新阈值。cnt_fg 0 || cnt_bg 0是必要的保护否则会出现除零异常。迭代上限 10 次是经验值通常 3 到 5 次就收敛。参数说明init_th传入图像均值或上一帧阈值。如果传入上一帧阈值迭代次数通常减少到 2 到 3 次适合高帧率场景。注意迭代法在图像对比度低时可能震荡表现为阈值在两个值之间反复跳变这时候需要加一个阻尼系数比如th_new (th_new th) / 2。3.4 双峰法的峰谷检测与有效性判断双峰法先对直方图做平滑然后找两个最高峰再在峰之间找谷底。关键是有效性判断如果两峰间距太小或谷底不够深就放弃。// 双峰法返回阈值valid 输出是否有效 uint8_t calc_bimodal_threshold(uint16_t *hist, uint8_t *valid) { uint16_t smooth[256]; // 3 点滑动平均平滑直方图 for (int i 1; i 255; i) { smooth[i] (hist[i-1] hist[i] hist[i1]) / 3; } smooth[0] hist[0]; smooth[255] hist[255]; // 找两个最高峰要求间距至少 30 个灰度级 int peak1 0, peak2 0; for (int i 1; i 256; i) { if (smooth[i] smooth[peak1]) { peak2 peak1; peak1 i; } else if (smooth[i] smooth[peak2] abs(i - peak1) 30) { peak2 i; } } if (abs(peak1 - peak2) 30) { *valid 0; return 128; } // 在峰之间找谷底 int lo peak1 peak2 ? peak1 : peak2; int hi peak1 peak2 ? peak1 : peak2; int valley lo; for (int i lo; i hi; i) { if (smooth[i] smooth[valley]) valley i; } // 谷底深度判断谷底值应小于两峰较小值的 80% uint16_t min_peak smooth[peak1] smooth[peak2] ? smooth[peak1] : smooth[peak2]; if (smooth[valley] min_peak * 0.8) { *valid 0; return 128; } *valid 1; return (uint8_t)valley; }逻辑说明平滑是为了抑制噪声导致的假峰。找峰时要求两峰间距至少 30 个灰度级否则认为是同一峰的分裂。谷底深度判断用 80% 作为阈值如果谷底不够深说明前景背景没有明显分离双峰法失效。参数说明平滑窗口 3 点是经验值如果直方图噪声大可以加到 5 点。峰间距 30 是根据赛道黑白灰度差通常大于 50 来设定的如果赛道对比度低可以降到 20。谷底深度 80% 也是经验值实际调试时可以观察直方图如果谷底明显低于两峰这个条件很容易满足。3.5 大津法的遍历优化与隔帧计算大津法标准实现是遍历 256 个灰度级每个灰度级计算类间方差。优化思路是用累积直方图和累积灰度矩避免内层循环。// 大津法返回最佳阈值 uint8_t calc_otsu_threshold(uint16_t *hist, uint16_t total) { uint32_t sum_all 0; for (int i 0; i 256; i) sum_all (uint32_t)i * hist[i]; uint32_t sum_bg 0; uint16_t cnt_bg 0; uint32_t max_var 0; uint8_t best_th 0; for (int th 0; th 256; th) { cnt_bg hist[th]; if (cnt_bg 0) continue; uint16_t cnt_fg total - cnt_bg; if (cnt_fg 0) break; sum_bg (uint32_t)th * hist[th]; uint32_t sum_fg sum_all - sum_bg; // 计算类间方差 int32_t mean_bg sum_bg / cnt_bg; int32_t mean_fg sum_fg / cnt_fg; int32_t diff mean_bg - mean_fg; uint32_t var (uint32_t)cnt_bg * cnt_fg * diff * diff; if (var max_var) { max_var var; best_th th; } } return best_th; }逻辑说明sum_all是全图灰度总和sum_bg和cnt_bg在遍历中累积。类间方差公式用cnt_bg * cnt_fg * (mean_bg - mean_fg)^2避免了除法只在最后比较时用整数运算。diff * diff可能溢出 int32所以用uint32_t存储方差。参数说明total是总像素数必须准确传入。如果只处理 ROItotal是 ROI 像素数hist也必须是 ROI 的直方图。隔帧计算时可以每 2 帧或 3 帧调用一次calc_otsu_threshold中间帧沿用上一帧阈值。如果图像变化剧烈可以加一个变化检测当连续两帧均值差超过 10 时强制重新计算。3.6 五种算法的统一调用接口与阈值融合实际车上不会只跑一种算法常见做法是同时跑大津法和双峰法用有效性判断做融合。// 统一阈值计算接口返回最终阈值 uint8_t calc_final_threshold(uint8_t *img, uint16_t size, uint8_t last_th) { uint16_t hist[256]; uint8_t mean calc_histogram(img, hist, size); // 大津法作为主算法 uint8_t th_otsu calc_otsu_threshold(hist, size); // 双峰法作为验证 uint8_t valid 0; uint8_t th_bi calc_bimodal_threshold(hist, valid); // 融合策略双峰法有效且与大津法差值小于 20取平均 if (valid abs(th_bi - th_otsu) 20) { return (th_bi th_otsu) / 2; } // 大津法输出异常时用上一帧阈值平滑 if (abs(th_otsu - last_th) 50) { return (th_otsu last_th) / 2; } return th_otsu; }逻辑说明calc_histogram返回均值同时填充直方图供后续算法复用。大津法作为主算法双峰法作为交叉验证。如果双峰法有效且与大津法接近取平均可以进一步平滑阈值。如果大津法输出与上一帧差值超过 50说明图像可能突变用上一帧阈值做平滑避免阈值跳变导致巡线抖动。参数说明差值 20 和 50 都是经验值。差值 20 用于判断两种算法是否一致如果赛道对比度高可以降到 15如果对比度低可以升到 30。差值 50 用于判断大津法是否异常如果图像噪声大可以升到 60。last_th需要全局保存上一帧的最终阈值。4. 五种阈值算法在智能车上的避坑与排查记录4.1 阈值跳变导致舵机抖动现象车在直道上行驶时舵机高频抖动图像二值化结果在相邻帧之间黑白区域大面积翻转。原因大津法在直道图像上可能因为远处赛道消失导致直方图双峰不明显阈值在 100 到 140 之间跳变。每帧阈值不同二值化后的黑线位置左右偏移舵机跟着抖。解决加入阈值平滑用上一帧阈值做低通滤波。具体做法是th_final (th_new th_last * 3) / 4让阈值变化不超过每帧 5 个灰度级。同时检查大津法是否在直道上失效如果连续 5 帧阈值变化超过 20切换到百分比阈值兜底。4.2 双峰法在阴影下把背景切成前景现象车进入阴影区域后二值化图像全白黑线消失车直接冲出赛道。原因阴影导致直方图出现三个峰——阴影背景、正常背景、黑线。双峰法找到的两个最高峰可能是阴影背景和正常背景谷底落在两者之间阈值偏高黑线被归入背景。解决双峰法必须加有效性判断除了峰间距和谷底深度还要检查阈值是否落在合理范围。如果阈值高于图像均值的 1.5 倍直接判定无效。同时用大津法交叉验证如果大津法阈值比双峰法低 30 以上以双峰法无效处理。4.3 迭代法在低对比度图像上震荡现象图像对比度低时迭代法输出的阈值在 80 和 120 之间反复跳变二值化结果黑白交替。原因低对比度下前景和背景灰度分布重叠迭代法每次计算的平均值差异很小阈值在中间值附近震荡无法收敛到稳定点。解决加阻尼系数每次更新阈值时只取新阈值和旧阈值的中间值即th (th th_new) / 2。同时设迭代次数上限 5 次超过就强制退出。如果震荡仍然存在说明图像本身不适合迭代法切换到百分比阈值。4.4 大津法在远处弯道把全图切成背景现象车看到远处弯道时图像中黑线占比极小大津法阈值突然升高到 200 以上二值化后全白。原因黑线占比极小时直方图接近单峰大津法计算类间方差时把少量黑线像素归入背景反而使方差更大导致阈值偏向背景。解决加黑线占比判断。如果二值化后黑线像素少于总像素的 2%说明阈值过高强制把阈值下调 20 到 30。或者用上一帧阈值做限制如果当前阈值比上一帧高 40 以上取上一帧阈值加 20 作为最终阈值。4.5 百分比阈值在起跑线区域误切现象车经过起跑线或斑马线时图像中出现大面积黑色区域百分比阈值按累积分布切分后黑线被淹没在黑色区域中巡线失效。原因百分比阈值假设黑线占比固定但起跑线区域黑色像素占比从 15% 突增到 50% 以上累积分布分位点对应的灰度值大幅偏移。解决百分比阈值只作为降级方案正常巡线时不用。如果必须用加一个黑色区域面积检测当二值化后黑色像素占比超过 40% 时判定为特殊区域切换到固定阈值或上一帧阈值。同时用赛道元素识别逻辑跳过起跑线区域不依赖图像阈值。5. 阈值算法之外的进阶技巧用动态ROI和分区域阈值提升巡线稳定性五种阈值算法解决的是全局二值化问题但智能车图像里近处赛道和远处赛道的灰度分布差异很大。近处黑线清晰、背景白亮远处黑线模糊、背景偏灰。用同一个全局阈值处理全图往往近处切得好远处切得差。我后来习惯把图像分成上下两半分别计算阈值再在交界处做线性过渡。具体做法把 80×60 图像分成上 30 行和下 30 行分别统计直方图分别跑大津法得到th_top和th_bottom。然后对每一行阈值按行号线性插值th_row th_top (th_bottom - th_top) * row / 60。这样近处用低阈值保留黑线细节远处用高阈值抑制背景噪声。// 分区域阈值计算返回每行的阈值数组 void calc_region_threshold(uint8_t *img, uint8_t *th_row) { uint16_t hist_top[256] {0}, hist_bot[256] {0}; // 上半区域统计 for (int y 0; y 30; y) for (int x 0; x IMG_W; x) hist_top[img[y * IMG_W x]]; // 下半区域统计 for (int y 30; y 60; y) for (int x 0; x IMG_W; x) hist_bot[img[y * IMG_W x]]; uint8_t th_top calc_otsu_threshold(hist_top, 30 * IMG_W); uint8_t th_bot calc_otsu_threshold(hist_bot, 30 * IMG_W); // 线性插值生成每行阈值 for (int y 0; y 60; y) { th_row[y] th_top (int)(th_bot - th_top) * y / 59; } }这段代码的计算量是全局大津法的两倍因为要统计两个直方图并跑两次大津法。在 80×60 图像上总操作数约 10000 次50fps 下 50 万次/秒对 100MHz 单片机有压力。优化方法是隔帧计算或者只在上半区域跑大津法下半区域用全局阈值加固定偏移。分区域阈值的收益在弯道和坡道上很明显。弯道时远处赛道倾斜黑线在图像上半部分变细全局阈值容易把远处黑线切丢分区域阈值让上半部分阈值自适应降低保留远处黑线。坡道时近处赛道变宽下半部分阈值升高抑制近处反光。另一个技巧是动态 ROI。智能车巡线只关心图像中间偏下的区域左右两侧和顶部可以裁掉。把 ROI 从 80×60 缩小到 60×40像素数从 4800 降到 2400大津法计算量减半帧率可以提升。ROI 的边界根据上一帧黑线位置动态调整如果黑线偏左ROI 左边界左移如果黑线偏右ROI 右边界右移。这样既减少计算量又保证黑线始终在 ROI 内。我踩过最深的坑是过度依赖单一阈值算法。早期只用大津法在实验室跑得好好的比赛现场遇到顶光加侧光大津法阈值直接跑飞车在第一圈就冲出赛道。后来改成大津法加双峰法交叉验证再加百分比阈值兜底三套算法同时跑用有效性判断切换才稳定下来。阈值算法没有银弹多算法融合和动态调整才是正路。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑