流程图
代码思路完全按照流程图实现,其中的参数可以根据实际要处理的环境调整
代码
#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/highgui.hpp"
#include "math.h"
using namespace std;
using namespace cv;
double getLineLength(Point p1,Point p2){
return sqrt(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2));
}
double getLineGradient(Point p1,Point p2){
if(p1.x==p2.x||p1.x-p2.x==0){
p1.x = p1.x + 1;
}
return (p1.y-p2.y)/(p1.x-p2.x);
}
//处理离群直线
void handlingOutlierLines(vector<Vec4i> &Lines,size_t threshold){
double maxLength,minLength;
maxLength = getLineLength(Point(Lines[0][0],Lines[0][1]),Point(Lines[0][2], Lines[0][3]));
minLength = getLineLength(Point(Lines[0][0],Lines[0][1]),Point(Lines[0][2], Lines[0][3]));
cout<<"enter handlingOutlierLines"<<endl;
int number = Lines.size();
while(number>threshold){
double mean = 0.0;
double sum = 0.0;
for(size_t i=0;i<number;i++){
if(getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]))>maxLength){
maxLength = getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
}
if(getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]))<minLength){
minLength = getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
}
sum += getLineGradient(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
}
mean = sum/number;
cout<<"mean: "<<mean<<endl;
vector<Vec4i>::iterator it = Lines.begin();
while(it!=Lines.end()){
Vec4i vec = *it;
double gradient = getLineGradient(Point(vec[0],vec[1]),Point(vec[2], vec[3]));
if(fabs(gradient-mean)>fabs(20*mean)){
it = Lines.erase(it);
}else{
++it;
}
}
number = Lines.size();
}
cout<<"maxLength: "<<maxLength<<" minLength: "<<minLength<<"lineNumber: "<<Lines.size()<<endl;
};
//检测空洞
Mat connected_components_stat(Mat& image) {
// 二值化
Mat gray, binary;
cvtColor(image, gray, COLOR_BGR2GRAY);
threshold(gray, binary, 200, 255, THRESH_BINARY);
//开运算、闭运算
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
//morphologyEx(binary, binary, MORPH_DILATE, kernel);
//morphologyEx(binary, binary, MORPH_CLOSE, kernel);
imshow("binaryImage",binary);
//计算连通域
Mat labels = Mat::zeros(image.size(), CV_32S);
Mat stats, centroids;
int num_labels = connectedComponentsWithStats(binary, labels, stats, centroids, 8, 4);
//使用不同的颜色标记连通域
vector<unsigned char> colors(num_labels);
// 背景颜色
colors[0] = 0;
//将白色空洞染成黑色
for (int i = 1; i < num_labels; i++) {
Vec2d pt = centroids.at<Vec2d>(i, 0);
int x = stats.at<int>(i, CC_STAT_LEFT);
int y = stats.at<int>(i, CC_STAT_TOP);
int width = stats.at<int>(i, CC_STAT_WIDTH);
int height = stats.at<int>(i, CC_STAT_HEIGHT);
int area = stats.at<int>(i, CC_STAT_AREA);
if(area<=20||area>400||(y<image.rows/2&&area>100)){
colors[i] = 0;
}else{
colors[i] = 255;
}
}
Mat result = Mat::zeros(image.size(),CV_8U);
int w = image.cols;
int h = image.rows;
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
int label = labels.at<int>(row, col);
if (label == 0) continue;
result.at<unsigned char>(row, col) = colors[label];
}
}
imshow("result", result);
return result;
}
void detectWhiteDottedLine(string imgPath){
Mat image = imread(imgPath);
imshow("image",image);
Mat result;
result = connected_components_stat(image);
// Mat gaussImage;
// GaussianBlur(binaryImage,gaussImage,Size(3,3),0,0);
// imshow("gaussImage",gaussImage);
//
Mat cannyImage;
Canny(result,cannyImage,235,250);
imshow("cannyImage",cannyImage);
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
//morphologyEx(cannyImage,cannyImage,MORPH_DILATE,kernel);
Mat OutputImage = imread(imgPath);
vector<Vec4i> Lines;
HoughLinesP(cannyImage, Lines, 1, CV_PI / 360,30, 20, 20);
cout<<"检测到的直线的数量是:"<<Lines.size()<<endl;
//handlingOutlierLines(Lines,Lines.size()-1);
double max;
Vec4i max1,max2;
max = getLineLength(Point(Lines[0][0],Lines[0][1]),Point(Lines[0][2], Lines[0][3]));
max1 = {Lines[0][0],Lines[0][1],Lines[0][2],Lines[0][3]};
for (size_t i = 0; i < Lines.size(); i++)
{
double lineLength = getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
if(lineLength>max){
max = lineLength;
max2 = max1;
max1 ={Lines[i][0],Lines[i][1],Lines[i][2], Lines[i][3]};
}
if(lineLength>0){
line(OutputImage, Point(Lines[i][0], Lines[i][1]), Point(Lines[i][2], Lines[i][3]), Scalar(0, 0, 255), 2, 8);
}
}
cout<<max1<<" "<<max2<<" "<<max<<endl;
imshow("OutputImage", OutputImage);
waitKey(0);
destroyAllWindows();
}
void detectWhiteDottedLine2(string imgPath){
Mat image = imread(imgPath,IMREAD_GRAYSCALE);
imshow("image",image);
Mat binaryImage;
threshold(image,binaryImage,220,250,THRESH_BINARY);
imshow("binaryImage",binaryImage);
Mat morphImage;
Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3), Point(-1, -1));
morphologyEx(binaryImage,morphImage,MORPH_DILATE,kernel);
imshow("morphImage",morphImage);
//
// Mat gaussImage;
// GaussianBlur(binaryImage,gaussImage,Size(3,3),0,0);
// imshow("gaussImage",gaussImage);
Mat cannyImage;
Canny(morphImage,cannyImage,235,250);
imshow("cannyImage",cannyImage);
//计算连通域
Mat labels = Mat::zeros(image.size(), CV_32S);
Mat stats, centroids;
int num_labels = connectedComponentsWithStats(cannyImage, labels, stats, centroids, 8, 4);
//使用不同的颜色标记连通域
vector<unsigned char> colors(num_labels);
// 背景颜色
colors[0] = 0;
//将白色空洞染成黑色
for (int i = 1; i < num_labels; i++) {
Vec2d pt = centroids.at<Vec2d>(i, 0);
//int x = stats.at<int>(i, CC_STAT_LEFT);
int y = stats.at<int>(i, CC_STAT_TOP);
//int width = stats.at<int>(i, CC_STAT_WIDTH);
//int height = stats.at<int>(i, CC_STAT_HEIGHT);
int area = stats.at<int>(i, CC_STAT_AREA);
if(area<=35){
colors[i] = 0;
}else{
colors[i] = 255;
}
}
Mat result = Mat::zeros(image.size(),CV_8U);
int w = image.cols;
int h = image.rows;
for (int row = 0; row < h; row++) {
for (int col = 0; col < w; col++) {
int label = labels.at<int>(row, col);
if (label == 0) continue;
result.at<unsigned char>(row, col) = colors[label];
}
}
imshow("result", result);
// Mat gaussImage;
// GaussianBlur(result,gaussImage,Size(3,3),0,0);
// imshow("gaussImage",gaussImage);
Mat OutputImage = imread(imgPath);
vector<Vec4i> Lines;
HoughLinesP(result, Lines, 1, CV_PI / 360,50, 20, 30);
cout<<"检测到的直线的数量是:"<<Lines.size()<<endl;
//handlingOutlierLines(Lines,Lines.size()-1);
double max;
Vec4i max1,max2;
max = getLineLength(Point(Lines[0][0],Lines[0][1]),Point(Lines[0][2], Lines[0][3]));
max1 = {Lines[0][0],Lines[0][1],Lines[0][2],Lines[0][3]};
for (size_t i = 0; i < Lines.size(); i++)
{
double lineLength = getLineLength(Point(Lines[i][0],Lines[i][1]),Point(Lines[i][2], Lines[i][3]));
if(lineLength>max){
max = lineLength;
max2 = max1;
max1 ={Lines[i][0],Lines[i][1],Lines[i][2], Lines[i][3]};
}
if(lineLength>10&&lineLength<90){
line(OutputImage, Point(Lines[i][0], Lines[i][1]), Point(Lines[i][2], Lines[i][3]), Scalar(0, 0, 255), 2, 8);
}
}
cout<<max1<<" "<<max2<<" "<<max<<endl;
imshow("OutputImage", OutputImage);
waitKey(0);
destroyAllWindows();
}
double getAngle(Point angle,Point edge1,Point edge2){
double vec1_x = edge1.x - angle.x;
double vec1_y = edge1.y - angle.y;
double vec2_x = edge2.x - angle.x;
double vec2_y = edge2.y - angle.y;
double cos = (vec1_x*vec2_x+vec1_y*vec2_y)/( sqrt(pow(vec1_x,2)+pow(vec1_y,2)) * sqrt(pow(vec2_x,2)+pow(vec2_y,2)) );
double result = acos(cos)/CV_PI*180;
return result;
}
void detectFishboneLine(string imgPath){
Mat image = imread(imgPath);
Mat grayImage;
cvtColor(image,grayImage,COLOR_RGB2GRAY);
Mat binaryImage;
threshold(grayImage,binaryImage,210,255,THRESH_BINARY);
imshow("binaryImage",binaryImage);
Mat dst0 = Mat::zeros(binaryImage.size(), CV_8UC3);
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(binaryImage, contours, hierarchy, RETR_LIST, CHAIN_APPROX_SIMPLE);
Mat contoursImage = image.clone();
drawContours(contoursImage,contours,-1,Scalar(0,0,0),3);
imshow("contoursImage",contoursImage);
vector<vector<Point>> points(contours.size());
for(int i = 0; i < contours.size(); i++){
approxPolyDP(contours[i],points[i],3, true);
}
for(int i = 0; i < contours.size(); i++){
if(points[i].size()==4){
Point p1 = points[i][0];
Point p2 = points[i][1];
Point p3 = points[i][2];
Point p4 = points[i][3];
double sum = 0;
double a1 = getAngle(p1,p2,p4);
double a2 = getAngle(p2,p1,p3);
double a3 = getAngle(p3,p2,p4);
double a4 = getAngle(p4,p3,p1);
sum = a1 + a2 + a3 + a4;
if(sum>300&&sum<400){
//cout<<"###"<<i<<points[i]<<endl;
drawContours(image,points,i,Scalar(0,255,255),3);
}
}
}
imshow("output", image);
waitKey(0);
destroyAllWindows();
}
int main() {
string imgPath = ".\imageProcess\\xx3.png";
string imgPath1 = ".\imageProcess\\ygx1.png";
int choice;
cout<<"Detect white dotted line input 1, detect fishbone line input 2:"<<endl;
cin>>choice;
if(choice==1){
cout<<"Enter the picture path + picture name:"<<endl;
cin>>imgPath;
detectWhiteDottedLine2(imgPath);
}else{
cout<<"Enter the picture path + picture name:"<<endl;
cin>>imgPath1;
detectFishboneLine(imgPath1);
};
return 0;
}
效果图
因为是同一套代码使用不同的测试图,所以检测效果不是特别好。如果是固定的路段或者是可以截取到比较固定的ROI的话,那么针对性地进行调参,结果会更好一些。
参考文章
python+opencv车道线,实线虚线的检测
入门版的车道线检测(python+opencv)
OpenCV实战案例——车道线识别
HoughLinesP参数设置(看完还不会你砍死我!!!)
连通域标记图像ConnectedComponents
OpenCV C++(九)----几何形状的检测和拟合
approxPolyDP(拟合多边形)