1290 字
6 分钟
RM装甲板识别 - 02 装甲板颜色提取
本阶段核心API清单
| API | 作用 |
|---|---|
cv::split() | 分离BGR通道 |
cv::cvtColor() | 颜色空间转换 |
cv::inRange() | 颜色范围筛选 |
cv::threshold() | 简单阈值二值化 |
cv::subtract() | 通道相减 |
cv::bitwise_or/and() | 位运算组合mask |
核心思路
装甲板的灯条有两个特点:
- 特定颜色(红色或蓝色)
- 高亮度(灯条是发光的)
通道相减法
- 原理: BGR图像中,红色区域R通道值高、B通道值低,相减后红色凸显
- 优点: 速度极快(1-2ms),实时性好
- 缺点: 易受环境光干扰,需要后续强筛选
- 适用: 比赛实战、光照稳定环境
HSV筛选法
- 原理: HSV分离了颜色和亮度,可精确筛选颜色范围
- 优点: 抗干扰能力强,准确度高
- 缺点: 速度较慢(5-10ms),参数需调优
- 适用: 复杂光照、高精度需求
1. cv::split() - 分离BGR通道
基础用法
#include <opencv2/opencv.hpp>#include <vector>
int main() { cv::Mat img = cv::imread("armor.jpg"); // 分离通道 std::vector<cv::Mat> channels; cv::split(img, channels); // 现在: // channels[0] = B通道(蓝色分量) // channels[1] = G通道(绿色分量) // channels[2] = R通道(红色分量) cv::waitKey(0);
return 0;}2. 通道相减法
提取红色
cv::Mat img = cv::imread("red_armor.jpg");// 分离通道std::vector<cv::Mat> channels;cv::split(img, channels);// 红色 = R通道 - B通道cv::Mat red;cv::subtract(channels[2], channels[0], red);// 二值化cv::Mat binary;cv::threshold(red, binary, 100, 255, cv::THRESH_BINARY);// 查看效果提取蓝色
// 蓝色 = B通道 - R通道cv::Mat blue;cv::subtract(channels[0], channels[2], blue);
cv::Mat binary_blue;cv::threshold(blue, binary_blue, 100, 255, cv::THRESH_BINARY);3. cv::cvtColor() - 颜色空间转换
常用转换代码
COLOR_BGR2GRAYBGR转灰度COLOR_BGR2HSVBGR转HSVCOLOR_BGR2RGBBGR转RGBCOLOR_HSV2BGRHSV转回BGR
HSV颜色空间详解
HSV把颜色分解为3个维度:
H (色调 Hue): 0-180├─ 0-10: 红色(第一段)├─ 100-124: 蓝色 装甲板蓝色└─ 170-180: 红色(第二段) 装甲板红色
S (饱和度 Saturation): 0-255└─ 100-255: 鲜艳色 灯条在这里
V (明度 Value): 0-255└─ 100-255: 明亮 灯条在这里BGR转HSV
cv::Mat img = cv::imread("armor.jpg");cv::Mat hsv;cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);4. cv::inRange() - 颜色范围筛选
src输入图像(通常是HSV)lowerb下界(Scalar(H_min, S_min, V_min))upperb上界(Scalar(H_max, S_max, V_max))dst输出二值图(在范围内=255,否则=0)
提取红色(完整版)
cv::Mat img = cv::imread("red_armor.jpg");cv::Mat hsv;cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);
// 红色的H值跨越了0-180的边界!需要分两段// 第一段: H=0-10cv::Scalar lower_red1(0, 100, 100); // (H, S, V)cv::Scalar upper_red1(10, 255, 255);
// 第二段: H=170-180cv::Scalar lower_red2(170, 100, 100);cv::Scalar upper_red2(180, 255, 255);
// 分别提取两段cv::Mat mask1, mask2, red_mask;cv::inRange(hsv, lower_red1, upper_red1, mask1);cv::inRange(hsv, lower_red2, upper_red2, mask2);
// 合并两段(位运算:或)cv::bitwise_or(mask1, mask2, red_mask);提取蓝色
// 蓝色只需要一段cv::Scalar lower_blue(100, 100, 100); // H=100-130cv::Scalar upper_blue(130, 255, 255);
cv::Mat blue_mask;cv::inRange(hsv, lower_blue, upper_blue, blue_mask);参数调优技巧
创建滑块实时调参:
#include <opencv2/opencv.hpp>
// 全局变量int h_min = 0, h_max = 10;int s_min = 100, s_max = 255;int v_min = 100, v_max = 255;
void on_trackbar(int, void*) { // 回调函数,每次滑块变化时调用}
int main() { cv::Mat img = cv::imread("armor.jpg"); cv::Mat hsv; cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);
// 创建窗口 cv::namedWindow("调参");
// 创建滑块 cv::createTrackbar("H min", "调参", &h_min, 180, on_trackbar); cv::createTrackbar("H max", "调参", &h_max, 180, on_trackbar); cv::createTrackbar("S min", "调参", &s_min, 255, on_trackbar); cv::createTrackbar("S max", "调参", &s_max, 255, on_trackbar); cv::createTrackbar("V min", "调参", &v_min, 255, on_trackbar); cv::createTrackbar("V max", "调参", &v_max, 255, on_trackbar);
while (true) { cv::Scalar lower(h_min, s_min, v_min); cv::Scalar upper(h_max, s_max, v_max);
cv::Mat mask; cv::inRange(hsv, lower, upper, mask);
cv::imshow("原图", img); cv::imshow("mask", mask);
if (cv::waitKey(30) == 27) break; }
// 打印最终参数 std::cout << "最优参数:" << std::endl; std::cout << "lower: (" << h_min << ", " << s_min << ", " << v_min << ")" << std::endl; std::cout << "upper: (" << h_max << ", " << s_max << ", " << v_max << ")" << std::endl;
return 0;}5. cv::threshold() - 简单阈值二值化
THRESH_BINARY| 标准二值化THRESH_BINARY_INV| 反向二值化THRESH_TRUNC| 截断THRESH_TOZERO| 低于阈值归零
配合通道相减使用
// 提取红色cv::Mat red;cv::subtract(channels[2], channels[0], red);
// 阈值二值化cv::Mat binary;cv::threshold(red, binary, 100, 255, cv::THRESH_BINARY);// 像素 > 100 → 255(白)// 像素 <= 100 → 0(黑)自适应阈值(应对光照不均)
cv::Mat gray;cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::Mat adaptive;cv::adaptiveThreshold(gray, adaptive, 255, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY, 11, 2);// blockSize=11: 邻域大小// C=2: 常数调整值6. cv::bitwise 位运算
常用函数
void cv::bitwise_or(InputArray src1, InputArray src2, OutputArray dst);void cv::bitwise_and(InputArray src1, InputArray src2, OutputArray dst);void cv::bitwise_not(InputArray src, OutputArray dst);void cv::bitwise_xor(InputArray src1, InputArray src2, OutputArray dst);合并多个mask
// 红色有两段,需要合并cv::Mat red_mask1, red_mask2, red_final;cv::inRange(hsv, lower_red1, upper_red1, red_mask1);cv::inRange(hsv, lower_red2, upper_red2, red_mask2);cv::bitwise_or(red_mask1, red_mask2, red_final);提取同时满足多个条件的区域
// 同时是红色 且 亮度很高cv::Mat color_mask, brightness_mask, final_mask;cv::inRange(hsv, lower_red, upper_red, color_mask);cv::threshold(hsv_channels[2], brightness_mask, 200, 255, cv::THRESH_BINARY);cv::bitwise_and(color_mask, brightness_mask, final_mask);反转mask
cv::Mat mask, inverted;cv::inRange(hsv, lower, upper, mask);cv::bitwise_not(mask, inverted); // 黑白反转完整实战代码
方案1: 通道相减法(快速)
#include <opencv2/opencv.hpp>
cv::Mat extractRedFast(const cv::Mat& img) { std::vector<cv::Mat> channels; cv::split(img, channels);
// R - B cv::Mat red; cv::subtract(channels[2], channels[0], red);
// 二值化 cv::Mat binary; cv::threshold(red, binary, 100, 255, cv::THRESH_BINARY);
return binary;}
int main() { cv::Mat img = cv::imread("red_armor.jpg"); cv::Mat binary = extractRedFast(img);
cv::imshow("原图", img); cv::imshow("红色提取", binary); cv::waitKey(0);
return 0;}方案2: HSV筛选法(精确)
#include <opencv2/opencv.hpp>
cv::Mat extractRedAccurate(const cv::Mat& img) { cv::Mat hsv; cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);
// 红色两段 cv::Scalar lower1(0, 100, 100); cv::Scalar upper1(10, 255, 255); cv::Scalar lower2(170, 100, 100); cv::Scalar upper2(180, 255, 255);
cv::Mat mask1, mask2, mask; cv::inRange(hsv, lower1, upper1, mask1); cv::inRange(hsv, lower2, upper2, mask2); cv::bitwise_or(mask1, mask2, mask);
return mask;}
cv::Mat extractBlue(const cv::Mat& img) { cv::Mat hsv; cv::cvtColor(img, hsv, cv::COLOR_BGR2HSV);
cv::Scalar lower(100, 100, 100); cv::Scalar upper(130, 255, 255);
cv::Mat mask; cv::inRange(hsv, lower, upper, mask);
return mask;}
int main() { cv::Mat img = cv::imread("armor.jpg");
cv::Mat red_mask = extractRedAccurate(img); cv::Mat blue_mask = extractBlue(img);
cv::imshow("原图", img); cv::imshow("红色mask", red_mask); cv::imshow("蓝色mask", blue_mask); cv::waitKey(0);
return 0;} RM装甲板识别 - 02 装甲板颜色提取
https://www.nanye404.top/posts/rm_opencv_02/ 部分信息可能已经过时