我正在尝试创建一个可以从摄像头检测交通标志的应用程序。然而,我在检测标志时遇到了一些困难。
由于交通标志的颜色可能不同,所以我希望先检测标志的形状。
我尝试先找到所有轮廓并尝试检测每个轮廓的形状。然而,标志的轮廓不够清晰,树木背景包含大量小轮廓,会影响检测。这样程序就无法找到交通标志的完整轮廓。我知道使用 openCV 中的 fastNlMeansDenoisingColored() 可以显着消除噪声并使检测变得更加准确。但对于实时处理来说太慢了。
我不仅检查圆形标志,还检查三角形或四边形标志。因此,我希望首先找到所有物体的轮廓,并尝试检查物体的形状。
这是我用来查找图片内部轮廓的代码,以及它的结果。我要开发一个iOS应用程序,所以它是在objective-c中。
- (cv::Mat)findSigns:(cv::Mat)src {
Mat edge;
Mat src_gray;
cvtColor(src, src_gray, COLOR_RGB2GRAY);
medianBlur(src_gray, src_gray, 5);
Canny(src_gray, edge, 80, 80, 3);
Mat kernel = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3,3));
Mat dilated;
cv::dilate(edge, dilated, kernel);
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierarchy;
RNG rng(12345);
cv::findContours(edge, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
Scalar color = Scalar(255, 0, 255);
for(int i = 0; i<contours.size(); i++){
std::vector<cv::Point> c = contours[i];
double area = cv::contourArea(c);
if (area > 100) {
drawContours(src, contours, i, color, 2, 8, hierarchy);
}
}
edge.release();
src_gray.release();
kernel.release();
dilated.release();
return src;
}
src图片:
由于轮廓太多,所以我尝试去除小面积的轮廓。这是结果。
此外,我还尝试检查是否 hierarchy[i][2] == -1 以查看是否有任何闭合轮廓。但它们都等于-1。
请问我应该如何检测交通标志的形状?我是否应该通过删除绿色对象来删除树背景以便于检测?
你已经在做一些事情上了!分割出交通标志(即从背景中区分出来)是一个具有挑战性的问题,特别是当这些标志可能会有不同的颜色和照明条件时。你的方法是一个良好的开端,这里有一些改进建议,以帮助你检测交通标志的形状:
1. 改进图像预处理
- 颜色空间转换: 不要仅仅依靠灰度图像。尝试使用 HSV 或 LAB 颜色空间,这些颜色空间可以更好地分离颜色信息,并且对光照变化不太敏感。你可以隔离与交通标志颜色(红色、蓝色、黄色等)相关的特定颜色范围。
- 自适应直方图均衡化 (CLAHE): 与标准直方图均衡不同,CLAHE 可以局部增强图像的对比度,这有助于突出显示在非均匀光照条件下的边缘。
2. 稳健的轮廓检测
- 基于梯度的边缘检测: 除了 Canny 之外,还可以尝试使用 Sobel 或 Scharr 算子来进行边缘检测。实验不同的算子和阈值,看看哪个最适合你的场景。
- 形态学运算: 你已经在使用膨胀来连接边缘。考虑使用形态学开运算(先腐蚀后膨胀)来消除小的噪声区域,同时保留标志的整体形状。
3. 形状检测和分析
-
轮廓近似:
使用
cv::approxPolyDP
来简化找到的轮廓,减少数据点,同时保留其整体形状。 -
形状分类:
一旦你有了近似的轮廓,就可以使用以下方法对其进行分类:
-
Hough 变换:
对于圆形标志,可以使用
cv::HoughCircles
。 -
角点检测:
对于三角形和矩形,使用
cv::goodFeaturesToTrack
等方法检测角点,然后根据角点的数量和相对位置对其进行分类。
-
Hough 变换:
对于圆形标志,可以使用
- 纵横比和面积约束: 根据形状对轮廓应用纵横比和面积阈值,以过滤掉误报。例如,交通标志通常具有一定的最小面积和一定的纵横比范围。
4. 提高性能
- 调整图像大小: 在全分辨率下处理视频帧可能会很耗时。考虑在应用图像处理技术之前先降低图像分辨率。
- 区域建议 (ROI): 如果你知道交通标志可能出现的大致区域(例如,图像的下半部分),则可以只处理该区域,从而减少计算量。
代码示例(C++,但你可以将其调整为 Objective-C)
Mat detectTrafficSigns(Mat src) {
// 转换为 HSV 颜色空间
Mat hsv;
cvtColor(src, hsv, COLOR_BGR2HSV);
// 定义你感兴趣的颜色范围(例如,红色)
Scalar lower_red1(0, 100, 100);
Scalar upper_red1(10, 255, 255);
Scalar lower_red2(170, 100, 100);
Scalar upper_red2(180, 255, 255);
// 创建颜色掩码
Mat mask1, mask2, mask;
inRange(hsv, lower_red1, upper_red1, mask1);
inRange(hsv, lower_red2, upper_red2, mask2);
bitwise_or(mask1, mask2, mask);
// 形态学开运算
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(5, 5));
morphologyEx(mask, mask, MORPH_OPEN, kernel);
// 边缘检测
Mat edges;
Canny(mask, edges, 50, 150);
// 查找轮廓
vector<vector<Point>> contours;
findContours(edges, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
// 分析轮廓
for (const auto &contour : contours) {
double area = contourArea(contour);
if (area > 100) {
// 近似轮廓
vector<Point> approx;
approxPolyDP(contour, approx, 0.02 * arcLength(contour, true), true);
// 根据角点的数量对形状进行分类
if (approx.size() == 3) {
// 三角形
drawContours(src, vector<vector<Point>>{approx}, -1, Scalar(0, 255, 0), 2);
} else if (approx.size() == 4) {
// 矩形或方形
drawContours(src, vector<vector<Point>>{approx}, -1, Scalar(255, 0, 0), 2);
} else if (approx.size() >= 8) {
// 圆形
drawContours(src, vector<vector<Point>>{approx}, -1, Scalar(0, 0, 255), 2);
}
}
}
return src;
}
请记住,这些只是一些一般性建议,可能需要根据的具体需求和挑战来试验不同的技术和参数。
标签:python,c++,ios,opencv,computer-vision From: 53279905