OPENCV实现色带检测
今天在家無聊,終于有時間寫下我的第一篇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. 對光線要求較強,在房間里比較暗效果不好
總結
以上是生活随笔為你收集整理的OPENCV实现色带检测的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于我了解5G后对5G的认识
- 下一篇: 自动化控制行业常见面试问题分析