opencv traincascade 使用

traincascade 是opencv训练自己的分类器的工具,具体使用的时候有很多出错点,具体见 http://blog.csdn.net/bomingzi/article/details/77123678

这说一下使用步骤 

第一: 准备正负样本,负样本建议自己采集,如果是别人提供的非常大的负样本,训练起来非常费时间、

这里说一下正负样本,正样本是包含你所要检测的图片的样本,但不要只包含你检测的图片,而且你会发现裁剪起来非常不好裁剪,这里我有一个方便裁剪的程序

#include "stdafx.h"
#include <opencv2\core\core.hpp>
#include <opencv2\highgui\highgui.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include <opencv.hpp>
#include <sstream>
#include <direct.h>
using namespace std;
using namespace cv;

Mat org, dst, tmp,img;
int num = 0;
int pic_x = 200;
int pic_y = 200;



void on_mouse(int event, int x, int y, int flags, void *ustc)
{
	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.x = pre_pt.x + pic_x;
		cur_pt.y = pre_pt.y + pic_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, 0, 255, 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);

		if (waitKey(0) == 13)
		{
			stringstream ss;
			string str;
			ss << "G:\\template\\sample\\" << num << ".jpg";
			ss >> str;
			imwrite(str, dst);
			num++;
			cout << "has saved  " << str << endl;
			destroyWindow("dst");
			destroyWindow("img");
		}
		else
		{
			cout << "dont save" << endl;
		}

	}
}


int main()
{
	VideoCapture cap(0);
	char key;
	while (1)
	{
		cap >> org;
		key=waitKey(30);
		imshow("ddd", org);

		if (key == 'a' )
		{
			org.copyTo(img);
			org.copyTo(tmp);
			namedWindow("img");
			setMouseCallback("img", on_mouse, 0);//调用回调函数    
			imshow("img", img);
		}

		if (key==27)
		{
			destroyWindow("ddd");
			break;
		}
	}
    return 0;
}

大致就是按下a开始选框框,然后按enter保存,路径是
G:\\template\\sample\\" << num << ".jpg";
这个大家根据自己的盘符保存。保存完之后,图片是这个格式的
int pic_x = 200;
int pic_y = 200;
要把它压缩到40*40以内,这个我以前的博客有提及,不再赘述。以前我是用opencv的程序,现在我用python来完成

附一下我的python代码吧,另外感叹下python真的很方便我当时没学python的时候为了写个归一化写了两个cpp。

import os
from PIL import Image
import sys

def use(f,w,h):
    fs = os.listdir(f)
    for f1 in fs:
        tmp_path = os.path.join(f, f1)
        if not os.path.isdir(tmp_path):
            print('文件: %s' % tmp_path)
            img = Image.open(tmp_path)
            out = img.resize((w, h))  # 改变大小
            out.save(tmp_path)
        else:
            print('文件夹:%s' % tmp_path)
            use(tmp_path.w,h)




if __name__ == "__main__":
    path = 'D:/picture/background'
    use(path,20,20)

然后是正样本统一命名,方便写标签,当然也可以不命名,反正有强大的python

再来一段python写txt的代码吧

import os

def traverse(f,w,h):
    with open("D:/pos.txt", "w") as f_t:
        fs = os.listdir(f)
        for f1 in fs:
            tmp_path = os.path.join(f, f1)
            #print(f1)
            if not os.path.isdir(tmp_path):
                print('文件: %s' % tmp_path)
                f_t.write(f1+' '+str(1)+' '+str(0)+' '+str(0)+' '+str(w)+' '+str(h)+'\n')
            else:
                print('文件夹:%s' % tmp_path)
                traverse(tmp_path,w,h)




if __name__ == "__main__":
    path = 'D:/picture/background'
    traverse(path,20,20)
这样正样本的txt和图片都准备好了,把他们放到一个文件夹下



类似于这样 

负样本采集完之后不需要处理,但是不要像素太大,否则训练时间太长。 负样本写txt,这个直接用windows命令就可以

然后开始创建正样本的vec,我把他写成一个txt,每次使用的时候从命令窗口里面输入。在正样本文件夹下输入

opencv_createsamples.exe -info pos.txt -vec pos.vec -num 68 -w 40 -h 40

这里的num就是你的图片的数量,和后面的不一样。

会创建一个pos.vec文件,这个文件是traincascade用的,把它拿出来,方便traincascade用

这里要说一下neg,txt 因为我的neg.txt在neg图片文件夹的外部,所以我的neg.txt是这样写的

如果你的neg文件夹在里面直接写名字就好

然后是traincascade

opencv_traincascade.exe -data DATA -vec pos.vec -bg neg.txt -numPos 60 -numNeg 200 -numStages 11 -w 40 -h 40 -minHitRate 0.99 -featureType HAAR -mode ALL

这里解释下参数的意思,

-data 就是你的分类器放的位置,一定要是空文件夹

-vev 就是你刚才创建的vec的位置

-bg neg.txt 是负样本标签的txt

-numPos 取值小于图片数量,具体见之前的博客

-numNeg 负样本图片的数量

-numStages 这个有特殊的要求,具体见之前的博客

-w -h 这个是你正样本标签里面写的w和h,不能超过40,不然会报错

-minHitRate 这个也有要求,越接近于1越好。

-featureType 可以选择LBP HAAR HOG特征,下文会有赘述

-mode 只有HAAR特征有这个参数,这个mode资源够的话尽量选ALL,如果你是32位的系统,选ALL可能会内存不够。

参数就是这样,训练完之后会有这个文件

下面就开始来用这个文件,用的方法

#include "stdafx.h"
#include "prepare.h"

using namespace std;
using namespace sl;


Camera zed;
cv::Mat image_ocv;
cv::Mat image_gray;

void on_mouse(int event, int x, int y, int flags, void *ustc)
{
	if (event == CV_EVENT_LBUTTONDOWN)//左键按下,读取初始坐标,并在图像上该点处划圆 
	{
		cv::Vec4f data;
		sl::Mat depth, point_cloud;
		zed.retrieveMeasure(depth, MEASURE_DEPTH);
		zed.retrieveMeasure(point_cloud, MEASURE_XYZRGBA);
		sl::float4 point_cloud_value;
		point_cloud.getValue(x, y, &point_cloud_value);
		float distance = sqrt(point_cloud_value.x*point_cloud_value.x + point_cloud_value.y*point_cloud_value.y + point_cloud_value.z*point_cloud_value.z);
		printf("Distance to Camera at (%d, %d): %f m\n", x, y, distance);

		//cv::cvtColor(image_ocv, image_gray, CV_RGB2GRAY);
		//int gray_data = image_gray.at<unsigned char>(x, y);

		//cv::imshow("gray", image_gray);
		//cv::waitKey(1000);
		//cv::destroyWindow("gray");
		//cout << "depth:    " << image_gray.depth() << endl;
		//cout << "the gray is     " << gray_data << endl;

		//data = image_ocv.at<cv::Vec4f>(x, y);
		//int gray = data[0] * 0.299 + data[1] * 0.587 + data[2] * 0.114;
		//cout << "the gray is  " << gray << endl;
		//printf("%d			%d			%d", data[0], data[1], data[2]);

		/*
		sl::Mat depth_zed(zed.getResolution(), MAT_TYPE_32F_C1);
		cv::Mat depth_ocv = slMat2cvMat(depth_zed);
		zed.retrieveMeasure(depth_zed, MEASURE_DEPTH);
		std::cout << depth_ocv.at<float>(x, y) << std::endl;
		*/
	}
}


float get_deepth(int x, int y)
{
	cv::Vec4f data;
	sl::Mat depth, point_cloud;
	zed.retrieveMeasure(depth, MEASURE_DEPTH);
	zed.retrieveMeasure(point_cloud, MEASURE_XYZRGBA);
	sl::float4 point_cloud_value;
	point_cloud.getValue(x, y, &point_cloud_value);
	float distance = sqrt(point_cloud_value.x*point_cloud_value.x + point_cloud_value.y*point_cloud_value.y + point_cloud_value.z*point_cloud_value.z);
	printf("Distance to Camera at (%d, %d): %f m\n", x, y, distance);
	return distance;
}



int main()
{
	/*
	cv::Mat src;
	src = cv::imread("G:\\pic\\timg.jpg");
	fitting_rect(src);
	cv::waitKey(0);
	*/

	std::string szName = "NameOfMappingObject";
	HANDLE hMapFile = CreateFileMapping(
		INVALID_HANDLE_VALUE,	//物理文件句柄
		NULL,					//默认安全级别
		PAGE_READWRITE,			//可读可写
		0,						//高位文件大小
		BUF_SIZE,				//地位文件大小
		stringToLPCWSTR(szName)		//共享内存名称
	);
	char *pBuf = (char *)MapViewOfFile(
		hMapFile,			//共享内存的句柄
		FILE_MAP_ALL_ACCESS,//可读写许可
		0,
		0,
		BUF_SIZE
	);






	string xmlPath = "G:\\traincascade\\DATA\\cascade.xml";
	cv::CascadeClassifier cas;
	cas.load(xmlPath);
	if (cas.empty())
	{
		std::cerr << "load detector failed!!!" << std::endl;
		return -1;
	}
	

	InitParameters init_params;
	init_params.depth_mode = DEPTH_MODE_PERFORMANCE;
	init_params.camera_resolution = RESOLUTION_HD2K;
	init_params.coordinate_units = UNIT_MILLIMETER;

	ERROR_CODE err = zed.open(init_params);
	if (err != SUCCESS)
	{
		exit(-1);
		cout << "can not open the camera" << endl;
	}

	RuntimeParameters runtime_parameters;
	runtime_parameters.sensing_mode = SENSING_MODE_STANDARD; // Use STANDARD sensing mode

	sl::Mat image(zed.getResolution(), MAT_TYPE_8U_C3);
	cv::Mat image_left(image.getHeight(), image.getWidth(), CV_8UC3);
	char c = 0;

	while (1)
	{
		if (zed.grab(runtime_parameters) == SUCCESS)
		{ 
			zed.retrieveImage(image, VIEW_LEFT);	
			image_left = slMat2cvMat(image);
			cv::imshow("aaa", image_left);
			//cv::setMouseCallback("aaa", on_mouse, 0);
			if (c == 49)
			{
				std::vector<cv::Rect> rect;
				cv::Mat my_data;
				my_data = image_left.clone();
				//cv::cvtColor(my_data, my_data, CV_RGBA2BGR);
				//CV_HAAR_SCALE_IMAGE
				cas.detectMultiScale(my_data, rect, 1.1, 3, CV_HAAR_DO_CANNY_PRUNING, cv::Size(70, 70), cv::Size(110, 110));
				cv::Rect last_rect = fitting_rect(image_left);

				for (int i = 0; i < rect.size(); i++)
				{
					int distance_x;
					int distance_y;
					distance_x = abs(last_rect.x - rect[i].x);
					distance_y = abs(last_rect.y - rect[i].y);
					if (distance_x < 30)
					{
						cv::rectangle(my_data, rect[i], cv::Scalar(255, 65, 124), 2, 14);
						cout << " x :   " << rect[i].x << "  y:   " << rect[i].y << endl;
						float rect_deepth = get_deepth(rect[i].x, rect[i].y);

						cout << "start send" << endl;
						char szInfo[BUF_SIZE];
						sprintf_s(szInfo, "x   y   z     :     %d:     %d:     %f", rect[i].x, rect[i].y, rect_deepth);
						strncpy(pBuf, szInfo, BUF_SIZE - 1);
						pBuf[BUF_SIZE - 1] = '\0';
					}
					//cv::rectangle(my_data, rect[i], cv::Scalar(255, 65, 124), 2, 14);

				
				}
				cv::imshow("data", my_data);
				cv::waitKey(4000);
				cv::destroyWindow("data");
				//cas.detectMultiScale()
				
			}
			if (c == 50)
			{
				cv::Rect last_rect=fitting_rect(image_left);
				cv::waitKey(3000);
			}
			c = cv::waitKey(5);
			if (c==27)
			{
				cv::destroyAllWindows();	
				break;
			}
			
			
		}
		
	}
	zed.close();
	UnmapViewOfFile(pBuf);
	CloseHandle(hMapFile);
	cout << "alll ha benn end" << endl;
	
    return 0;
}

这个是zed相机的识别程序,可以参考着改成普通相机的。

然后来说一下关于LBP,HOG,HAAR的区别

HOG方向梯度直方图,最著名的HOG+svm的行人检测,其实行人的轮廓特征是比较明显的,但是数目灯杆等会造成干扰,

毕竟边缘都是沿着一个方向的,所以还有了专门为了排除干扰的难负样本,HOG的核心是计算横坐标和纵坐标的方向梯度,

所以他的信息集中在轮廓的趋势,以及变换的频率(个人感觉),应用于一类物体的检测是比较好的,比如封口瓶?

LBP 局部二值模式,LBP的特点就是抽象化图像的特征,除去检测过程中很多不想要的细节,比如光照等,其提取特征的方法

有点类似于卷积,但是和卷积不同不会过分强调某一特征(卷积有很多卷积核),而且拥有旋转不变性。

HAAR特征,haar特征分为3类,边缘特征,线性特征,中心特征。(这些特征就很多了)HAAR用的比较多,因为他既可以关注到细节有能看到整体。

同时计算量也明显突出。对于一些稍微复杂的标识,HAAR有非常好的效果。我做过一个香烟的检测,HAAR的检测效果是比较好的。


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值