OPENCV实现色带检测

来源:互联网 发布:七日杀游戏端口被占用 编辑:程序博客网 时间:2024/05/19 23:59

今天在家无聊,终于有时间写下我的第一篇CSDN博客啦,前段时间帮一个老师做一个项目,用摄像头检测地上的色带,输出角度帮助机器人寻迹。就想到用opencv来做,可以我不会啊尴尬,怎么办?看呗。这里对opencv点个赞!函数通俗易懂,环境配置也比较简单,英语阅读能力好的人都不用买书的,看文档就可以用得飞起啊~~

看了几天就把程序写好了,用得不好大家不喜勿碰,思路如下:


程序如下:

// camera.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>  
#include <opencv2/opencv.hpp>  
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include "stdio.h"
#include "findtape.h"


using namespace cv;
using namespace std;


int minVotenum=50;


int _tmain(int argc, _TCHAR* argv[])
{
VideoCapture cap0(0);//打开一个摄像头  
VideoCapture cap1(1);//打开一个摄像头 笔记本必须要两个,否则打不开外置摄像头
vector<Vec4i> lines,resultlines;//存储视频中直线的起点和终点
Mat source_frame,source_frame1,gray_frame;//新建两帧图像 
int frame_width,frame_height;
namedWindow("视频",WINDOW_AUTOSIZE);//新建一个图像窗口


    if(!cap0.isOpened()) //判断是否打开 
    {  
cerr<<"Can not open a camera or file."<<endl;
        return -1;  
    }
//获取视频的宽和高
frame_height=(int)cap0.get(CV_CAP_PROP_FRAME_HEIGHT);
frame_width=(int)cap0.get(CV_CAP_PROP_FRAME_WIDTH);


    

//namedWindow("灰度视频",WINDOW_AUTOSIZE);//新建一个图像窗口
    bool stop = false;  
    while(!stop)  
    {  
        cap0>>source_frame;//从视频一副图片 
cap1>>source_frame1;//从视频一副图片  这句没用的,为了打开外置摄像头而已

cvtColor(source_frame, gray_frame, CV_BGR2GRAY);//转换成灰度图像  
        GaussianBlur(gray_frame, gray_frame, Size(7,7), 1.5, 1.5,BORDER_DEFAULT); //高斯滤波 
Canny(gray_frame, gray_frame, 50, 150, 3); //边缘检测 内部用到sobel计算梯度 可以调整阈值 想想怎么弄成自动阈值
//Hough变换
//检测直线,最小投票为50,线条不短于50,间隙不小于20
HoughLinesP( gray_frame, lines,1,CV_PI/360,minVotenum,50,20);
//找出目标直线 并计算角度 距离
if(findColoredTape(lines,resultlines))
{
drawDetectLines(source_frame,resultlines,Scalar(0,0,255));//画线


if((lines.size()>10)&&(minVotenum<200))
{
minVotenum++;
}
}
else
{
if(minVotenum>20)
{
minVotenum--;
}
//drawDetectLines(source_frame,lines,Scalar(0,255,0));//画线
}


display_information(source_frame,frame_width,frame_height, lines.size(),countAngel(resultlines),countLocation(resultlines));
        imshow("视频",source_frame);
//imshow("灰度视频",gray_frame); 

        if(waitKey(30) >=0)  
            stop = true;  
    }  
return 0;
}

findtape.h

#ifndef _FINDTAPE_H
#define _FINDTAPE_H
 
#include <opencv2/core/core.hpp>  


using namespace cv;
using namespace std;


void drawDetectLines(Mat& image,const vector<Vec4i>& lines,Scalar & color);//画线
float countAngel(vector<Vec4i>& resultlines);
Point countLocation(vector<Vec4i>& resultlines);


uchar findColoredTape(vector<Vec4i>& sourcelines,vector<Vec4i>& resultlines);
void display_information(Mat Frame,int width,int height,int linesnum,float angle,Point location);//在视频里显示信息


#endif


findtape.cpp

#include "stdafx.h"
#include "findtape.h"
#include<iostream>  
#include <opencv2/opencv.hpp>  
#include <opencv2/core/core.hpp>  
#include <opencv2/highgui/highgui.hpp>  
#include "stdio.h"
#include "math.h"


double ParallelThreshold=0.2;//平行阈值,小于这个值认为是平行 0-1 越小要求越高
double intersectThreshold=0.3;//相交阈值,大于这个值认为是相交 0-1 越大要求越高


//画直线,将目标的两条直线用红色。其余用绿色
void drawDetectLines( Mat& image,const vector<Vec4i>& lines,Scalar & color)
{
    
// 将检测到的直线在图上画出来
vector<Vec4i>::const_iterator it=lines.begin();
while(it!=lines.end())
{
Point pt1((*it)[0],(*it)[1]);
Point pt2((*it)[2],(*it)[3]);
line(image,pt1,pt2,color,2); 
//  线条宽度设置为2
++it;
}
}


//比较两条直线的长度
bool compareLine(Vec4i line1,Vec4i line2)
{
long int length1,length2;
length1= (line1[0]-line1[2])*(line1[0]-line1[2])+(line1[1]-line1[3])*(line1[1]-line1[3]);
length2= (line2[0]-line2[2])*(line2[0]-line2[2])+(line2[1]-line2[3])*(line2[1]-line2[3]);
if(length1>length2)
return 1;
else
return 0;
}
//判断两条直线是否平行
bool judgeParallel(Vec4i line1,Vec4i line2)
{
double slope1,slope2,slope3;//斜率
slope1=abs(line1[0]-line1[2]) / (abs(line1[1]-line1[3])+1);//两条直线的斜率
slope2=abs(line2[0]-line2[2]) / (abs(line2[1]-line2[3])+1);
slope3=abs(line1[0]-line2[0]) / (abs(line1[1]-line2[1])+1);//第一条直线的第一个点和第二条直线的第一个点,判断是否在延长线上
if((abs(slope1-slope2)<ParallelThreshold)&&(abs(slope1-slope3)>intersectThreshold))//这两个阈值可以改变
{
return 1;
}
else
{
return 0;
}
}




//找出色带
uchar findColoredTape(vector<Vec4i>& sourcelines,vector<Vec4i>& resultlines)
{
Vec4i temp;
int compare_lines;//进行比较的线的条数


//进行比较的直线数 最多为 compare_lines条
if(sourcelines.size()>=2)
{
compare_lines=sourcelines.size();
}
else
{
return 0;
}

//冒泡排序
//将所有直线按长度排序
for(int i=0;i<compare_lines-1;++i)
{
for(int j=i+1;j<compare_lines;++j)
{
//判断长度
if(compareLine(sourcelines[j],sourcelines[i])==1)
{
//容器类型的赋值
temp=sourcelines[i];
sourcelines[i] = sourcelines[j];
sourcelines[j]=temp;
}
}
}


//若检测到的直线数大于等于2,则取长度最长的compare_lines条两两进行匹配
//找出最接近平行,不是延长线,总长度最长的两条直线
for(int i=0;i<compare_lines-1;++i)
{
for(int j=i+1;j<compare_lines;++j)
{
if(judgeParallel(sourcelines[i],sourcelines[j]))
{
resultlines.clear();
resultlines.push_back(sourcelines[i]);
resultlines.push_back(sourcelines[j]);
return 1;
}
}
}
return 0;
}


float countAngel(vector<Vec4i>& resultlines)
{
float angle;
if(resultlines.size()==2)
{
Vec4i line;
float x,y;
line[0]=(resultlines[0][0]+resultlines[1][0])/2;
line[1]=(resultlines[0][1]+resultlines[1][1])/2;
line[2]=(resultlines[0][2]+resultlines[1][2])/2;
line[3]=(resultlines[0][3]+resultlines[1][3])/2;
x=line[0]-line[2];
y=line[1]-line[3];
angle=atan2f(y,x)*180/CV_PI-90;
if(angle<-180)
{
angle+=180;
}
else if(angle>180)
{
angle-=180;
}
return angle;
}
else
{
return 0;
}
}


//计算位置
Point countLocation(vector<Vec4i>& resultlines)
{
Point location;
if(resultlines.size()==2)
{
location.x=(resultlines[0][0]+resultlines[0][2]+resultlines[1][0]+resultlines[1][2])/4;
location.y=(resultlines[0][1]+resultlines[0][3]+resultlines[1][1]+resultlines[1][3])/4;
return location;
}
else
{
location.x=0;
location.y=0;
return location;
}
}


//在视频里显示信息
void display_information(Mat Frame,int width,int height,int linesnum,float angle,Point location)
{
    char strFrameSize[30],strLineNum[20],Angle[20];


//显示直线 信息
sprintf_s(strLineNum, "strLineNum: %0d",linesnum);
putText(Frame,strLineNum,Point(0,20),2,1,CV_RGB(25,200,25));


sprintf_s(Angle, "Angle: %.2f ",angle);
    putText(Frame,Angle,Point(0,50),2,1,CV_RGB(25,200,25));


if(location.x>width/2)
{
putText(Frame,"Right",Point(0,80),2,1,CV_RGB(25,200,25));
}
else
{
putText(Frame,"left",Point(0,80),2,1,CV_RGB(25,200,25));
}
}

效果如下:


检测的效果还可以,不足有以下几个:

1.没有进行颜色检测,所以遇到多条平行线的时候会检测错误

2.虽然阈值选择会自动改变,但是有时候还是会检测不出边缘

3. 对光线要求较强,在房间里比较暗效果不好

0 0
原创粉丝点击