opencv cascade级联分类器 自定义目标检测

废话少说,先上图:

    一直想训练一个目标检测的级联分类器,花了一天的时间阅读其他优秀博客,然后自己实践了一下,里面也遇到一些坑,希望能给阅读本文章的读者带来帮助。

    opencv 已经提供了训练好的人脸和眼睛的目标检测的xml文件 ,可以做到检测视频图像中是否有人脸  ,但无法做到 像dlib提供的人脸的具体特征点的定位

    但是 自定义目标检测 才是我们真正感兴趣的。

    实现流程:

      1、正负样本的获取

            1.1 正样本的抠图(代码在最后)

            1.2 负样本(背景)

      2、训练

      3、检测(代码在最后)

  正样本的抠图 :没用像objectmaker 或者 LabelImg之类的 目标截取软件 而是在vs中 自己写了一个  如下图所示:

   

    时间比较紧,具体过程就不一步一步阐述了,需要注意的坑 有 ,生成负样本描述文件的 文件名 最好是文件的绝对路径,这样就可以忽略 在不同的文件夹 命令行执行 opencv_traincascade.exe 所产生的错误 如:大家所常遇到的 pos count:consumed Train dataset for temp stage can not be filled

  执行训练的命令如下所示:



训练过程很短, 不超过一分钟, 样本 数量信息:正样本300个 ,负样本824个  

1 抠图代码 opencv 实现 

//

#include "stdafx.h"

#include "stdafx.h"
#include<opencv2\opencv.hpp>
#include <opencv2/core/core.hpp>    
#include <opencv2/highgui/highgui.hpp>  
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;

char filename[200];
String flodername = "C:/opencv/sources/apps/annotation/vs/annotation/postivephoto/";
int countss = 102;
cv::Mat org, dst, img, tmp ,frame;
void on_mouse(int event, int x, int y, int flags, void *ustc)//event鼠标事件代号,x,y鼠标坐标,flags拖拽和键盘操作的代号  
{
	static Point pre_pt(-1, -1);//初始坐标  
	static Point cur_pt(-1, -1);//实时坐标  
	char temp[16];
	if (event == CV_EVENT_LBUTTONDOWN)//左键按下,读取初始坐标,并在图像上该点处划圆  
	{
		org.copyTo(img);//将原始图片复制到img中  
		sprintf_s(temp, "(%d,%d)", x, y);
		pre_pt = Point(x, y);
		putText(img, temp, pre_pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0, 255), 1, 8);//在窗口上显示坐标  
		circle(img, pre_pt, 2, Scalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);//划圆  
		imshow("img", img);
	}
	else if (event == CV_EVENT_MOUSEMOVE && !(flags & CV_EVENT_FLAG_LBUTTON))//左键没有按下的情况下鼠标移动的处理函数  
	{
		img.copyTo(tmp);//将img复制到临时图像tmp上,用于显示实时坐标  
		sprintf_s(temp, "(%d,%d)", x, y);
		cur_pt = Point(x, y);
		putText(tmp, temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0, 255));//只是实时显示鼠标移动的坐标  
		imshow("img", tmp);
	}
	else if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON))//左键按下时,鼠标移动,则在图像上划矩形  
	{
		img.copyTo(tmp);
		sprintf_s(temp, "(%d,%d)", x, y);
		cur_pt = Point(x, y);
		putText(tmp, temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0, 255));
		rectangle(tmp, pre_pt, cur_pt, Scalar(0, 255, 0, 0), 1, 8, 0);//在临时图像上实时显示鼠标拖动时形成的矩形  
		imshow("img", tmp);
	}
	else if (event == CV_EVENT_LBUTTONUP)//左键松开,将在图像上划矩形  
	{
		org.copyTo(img);
		sprintf_s(temp, "(%d,%d)", x, y);
		cur_pt = Point(x, y);
		putText(img, temp, cur_pt, FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 0, 255));
		circle(img, pre_pt, 2, Scalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
		rectangle(img, pre_pt, cur_pt, Scalar(0, 255, 0, 0), 1, 8, 0);//根据初始点和结束点,将矩形画到img上  
		imshow("img", img);
		img.copyTo(tmp);
		//截取矩形包围的图像,并保存到dst中  
		int width = abs(pre_pt.x - cur_pt.x);
		int height = abs(pre_pt.y - cur_pt.y);
		if (width == 0 || height == 0)
		{
			printf("width == 0 || height == 0");
			return;
		}
		dst = org(Rect(min(cur_pt.x, pre_pt.x), min(cur_pt.y, pre_pt.y), width, height));
		namedWindow("dst");
		imshow("dst", dst);
		sprintf_s(filename, "postive%d.bmp", ++countss);
		String full_path = flodername + "/" + filename;
		resize(dst, dst, Size(24, 24));
		imwrite( full_path, dst);//图片保存到本工程目录中  
		cout << "postive" << countss << ".bmp has been stored" << endl;
		
	}
}



int main()
{
	


#if 1

	String flodername = "C:/opencv/sources/apps/annotation/vs/annotation/negtivephoto/";
	//sprintf_s(flodername, "%s", flod.c_str());
	cout << flodername << endl;
	// 打开摄像头
	VideoCapture capture(0);
	if (false == capture.isOpened())
	{
		cout << "camera open failed!" << endl;
		return -1;
	}



	while (true)
	{
		// 获取图片帧
		capture >> org;
		if (true == org.empty())
		{
			cout << "get no frame" << endl;
			break;
		}

		// 显示原始图片
		imshow("frame", org);
	
		//复制采集到的frame到resultRGB


		char key = (char)waitKey(10);
		if (27 == key)
		{
			break;
		}
		//		char key = (char)waitKey(10);
		if (key == 32)//按空格键进行拍照  
		{
			namedWindow("org");
			imshow("org", org);
			org.copyTo(tmp);
			org.copyTo(img);
			setMouseCallback("org", on_mouse, 0);
			while (1)
			{
				key = (char)waitKey(10);
				if (27 == key)
				{
					destroyWindow("org");
					destroyWindow("img");
					break;
				}
			}
		
		}
	}
#endif

	return 0;
}







2 、检测目标代码 opencv 实现 

// suqianfeng.cpp: 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <string>
#include <Windows.h>
#include<opencv2\opencv.hpp>
#include <opencv2/core/core.hpp>    
#include <opencv2/highgui/highgui.hpp>  
#include <iostream>
#include <stdio.h>
#include "opencv2/objdetect/objdetect.hpp"  
#include "opencv2/imgproc/imgproc.hpp"  
//#define unsigned char uchar
//#define unsigned int uint
#define MAXVALUE (255)
#define KERNEL_SIZE  5
using namespace std;
using namespace cv;


#include <iostream>  
#include <stdio.h>  

using namespace std;
using namespace cv;

/** Function Headers */
void detectAndDisplay(Mat frame);

/** Global variables */
//-- Note, either copy these two files from opencv/data/haarscascades to your current folder, or change these locations  
String obj_cascade_name = "cascade.xml";
CascadeClassifier obj_cascade;
string window_name = "Capture - Face detection";
RNG rng(12345);

/**
* @function main
*/
int main(void)
{
	VideoCapture capture;
	Mat frame;

	//-- 1. Load the cascades  
	if (!obj_cascade.load(obj_cascade_name)) { printf("--(!)Error loading\n"); 
	while (1)
	{
		;
	}
	return -1;
	};


	//-- 2. Read the video stream  
	capture.open(0);
	if (capture.isOpened())
	{
		for (;;)
		{
			capture >> frame;

			//-- 3. Apply the classifier to the frame  
			if (!frame.empty())
			{
				detectAndDisplay(frame);
			}
			else
			{
				printf(" --(!) No captured frame -- Break!"); break;
			}

			int c = waitKey(10);
			if ((char)c == 'c') { break; }

		}
	}
	return 0;
}

/**
* @function detectAndDisplay
*/
void detectAndDisplay(Mat frame)
{
	vector<Rect> rect;
	std::vector<Rect> obj;
	
	Mat frame_gray;

	cvtColor(frame, frame_gray, COLOR_BGR2GRAY);

	//-- Detect faces  
	obj_cascade.detectMultiScale(frame_gray, obj, 1.1, 2, 0 , Size(20, 30), Size(640, 480));

	for (size_t i = 0; i < obj.size(); i++)
	{
		if (obj[i].width > 20 && obj[i].height > 20)
		{
			rect.push_back(Rect(obj[i].x, obj[i].y, obj[i].width, obj[i].height));
			rectangle(frame, rect[i], Scalar(0, 0, 255), 2);
		}
		//Point center(obj[i].x + obj[i].width / 2, obj[i].y + obj[i].height / 2);
		//ellipse(frame, center, Size(obj[i].width / 2, obj[i].height / 2), 0, 0, 360, Scalar(255, 0, 0), 3, 8, 0);
	}
	rect.clear();
	//-- Show what you got  
	imshow(window_name, frame);
}


猜你喜欢

转载自blog.csdn.net/qq_28224015/article/details/80380439