本人初学OpenCV做的项目,这个项目初始是用于手影识别,即使用摄像头拍摄,双手在摄像头前做出相关手影动作,程序使用匹配算法识别出用户正在模仿的动物,并触发对应事件。其实也可以识别并匹配其他内容(由模板文件决定,最好是简单图形)。
①读取模板文件:
首先制作黑色背景白色图像的模板文件,统一格式命名后放入一文件夹内,用该函数读入文件夹目录,即可将文件夹内各个文件路径读入vector中。
void getFiles( string path, vector<string>& files) {
long hFile = 0; //文件句柄
struct _finddata_t fileinfo; //文件信息
string p;
if((hFile = _findfirst(p.assign(path).append("\\*").c_str(),&fileinfo)) != -1) {
do {
//----------1.1-将目录继续迭代----------
if((fileinfo.attrib & _A_SUBDIR))
{
if(strcmp(fileinfo.name,".") != 0 && strcmp(fileinfo.name,"..") != 0)
getFiles( p.assign(path).append("\\").append(fileinfo.name), files );
}
//----------1.2-将文件加入列表----------
else {
files.push_back(p.assign(path).append("\\").append(fileinfo.name) );
}
}
while(_findnext(hFile, &fileinfo) == 0);_findclose(hFile);
}
}
②模板旋转处理:
因为摄像头拍摄到的内容(如手影)并不一定是以预期角度拍摄的,可能会导致拍摄到的画面与模板图像内容一致却因角度不同而导致匹配失败,所以需将模板文件旋转不同角度后存入结构体中并与摄像头拍摄到的画面逐一匹配。
Mat revolve(Mat img, Point2f center, double ang, double scale)
{
Mat rotatemat= getRotationMatrix2D(center,ang,scale);
Mat imgR;
warpAffine(img,imgR,rotatemat,img.size());
return imgR;
}
//
③图像预处理:
对摄像头拍摄到的画面进行简单的预处理
void pretreat (Mat &img)
{
cvtColor(img,img,CV_BGR2GRAY); //转为灰度图
threshold(img,img,100,255,THRESH_BINARY); //二值化处理
medianBlur(img,img,3); //中值滤波
}
④图像识别轮廓并填充裁剪:
因为摄像头拍摄到的内容(如手影)并不一定是以预期距离拍摄的,可能会导致拍摄到的画面与模板图像内容一致却因比例不同而导致匹配失败,所以需将摄像头拍摄到的画面识别轮廓后进行填充或者裁剪以达到与模板图像同样的比例。(比例为轮廓中的内容占整张图片面积的比例)
bool padding (Mat &img)
{
Cflag = false;
AreaMax=0;
xMin=10000;
yMin=10000;
xMax=0;
yMax=0;
//----------4.1-寻找轮廓------------
vector<vector<Point>> contours; //定义二维向量,用于存轮廓坐标
vector<Vec4i> hierarchy; //定义一组向量,每个元素包含4个int型
findContours(img,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE,Point());
imgFil=Mat::zeros(img.size(),CV_8UC1); //创建与img尺寸相同的单通道灰度图,初始化为全黑
//----------4.2-筛选轮廓------------
for(i=0;i<contours.size();i++) //i为轮廓序号
{
Area = contourArea(contours[i], true); //求轮廓所包含图形面积
if ((Area > 1000)&&(Area > AreaMax)) //保留面积最大的轮廓(且轮廓面积必须大于1000)
{
Cflag = true;
AreaMax = Area;
k = i;
}
}
if (!Cflag) //未找到符合要求的轮廓
{
return Cflag;
}
else
{
//----------4.3-填充轮廓------------
xMax = 0;
yMax = 0;
xMin = 10000;
yMin = 10000;
drawContours(imgFil,contours,k,Scalar(255),-1,8,hierarchy,CV_RETR_LIST); //绘制轮廓
for(int j = 0;j < contours[k].size();j++)
{
x = contours[k][j].x; //轮廓点横坐标
y = contours[k][j].y; //轮廓点纵坐标
if (x > xMax