转载自:https://blog.csdn.net/luoyun614/article/details/42602037前言
时隔半年,才写这个步态能量图(二),一方面是这次的实现使用了不同的数据库,另一方面,则是有了编码和思路上的改进。
本次改进
本次主要有以下三个方面的改进:
1. 实现的数据库是自动化所是的CASIA datasetB,之前实现的是USF的数据库,前者的数据量要比后者大得多。而且可以涉及到不同的角度,不同的行为(大衣、背包和正常)。
2. 编码上全部自动化提取文件夹信息,后续应对其他数据库也可以修改该方法去自动读取信息。
3. 其实这个步态能量图的实现只是铺垫,后续更重要的是分析,看过我前面的文章的都知道我是做视频处理分析的,在实际情况中,人物的轮廓往往不是时时刻刻都是完整的,所以,为了使得训练的数据尽量接近真实的情况,我做了大胆的处理,就是不在检测周期,就是说不再除以周期,而是直接除以总的步态数,因为在实际场景中,当步态数量足够大的时候,可以等同于单个正常的步态能量图,从而减少因为遮挡、缺失等问题导致的图像偏差。 具体实现
1. 实现自动文件提取,主要是在main函数中处理。 // Create_GEI.cpp : 定义控制台应用程序的入口点。 // //用于自动生成CASIA DatasetB的GEI的图 //作者:罗韵 //时间:2015.01.08
#include "stdafx.h"
#include "string.h" #include <io.h> #include <stdio.h> #include <iostream>
#include <opencv2\opencv.hpp> #include <cv.h>
#include "GenerateGEI.h"
using namespace std; using namespace cv;
//@argv[1]:文件夹列表地址 int _tmain(int argc, char* argv[]) { char* rootpath=new char[100]; rootpath="G:\\CASIAdatasetB\\silhouettes\\"; //G:\\CASIA_datasetB\\silhouettes\\ 或者argv[1]输入也可以 cout<<rootpath<<endl; char* folder=new char[100]; //二级文件夹 char* filepath=new char[100]; //图片文件路径 char* folderpath=new char[100]; //图片所在文件夹路径 char* filename=new char[100]; //图片名称 for(int i=1;i<=124;i++) //以及目录有124个文件夹 { sprintf(folder,"%s\%03d",rootpath,i); cout<<folder<<endl; for(int j=1;j<=6;j++) { for(int k=0;k<=180;k+=18) { const char* p="*.*"; sprintf(filepath,"%s\\nm-%02d\\%03d\\%s",folder,j,k,p); cout<<filepath<<endl; long lf; _finddata_t file; //遍历该文件夹内所有文件 if((lf = _findfirst(filepath, &file))==-1l) //_findfirst返回的是long型; long __cdecl _findfirst(const char *, struct _finddata_t *) { cout<<"文件没有找到!\n"; system("pause"); } else { sprintf(folderpath,"%s\\nm-%02d\\%03d\\",folder,j,k); GenerateGEI* generate=new GenerateGEI(folderpath); //传入当前文件夹路径,方便作为生成的GEI文件名 cout<<"\n文件列表:\n"; while( _findnext( lf, &file ) == 0 )//int __cdecl _findnext(long, struct _finddata_t *);如果找到下个文件的名字成功的话就返回0,否则返回-1 { cout<<file.name<<endl; string str(file.name); int te = str.find(".png"); //指定文件类型 if(te != -1){ sprintf(filename,"%s\%s",folderpath,file.name); //图片 cout<<filename<<endl; //读入并处理图片 #ifdef _DEBUG IplImage* ipl=cvLoadImage(filename); cvNamedWindow("ipl",CV_WINDOW_AUTOSIZE); cvShowImage("ipl",ipl); cvWaitKey(-1); #endif Mat image=cv::imread(filename,0); //按单通道读入 #ifdef _DEBUG cv::imshow("image",image); cv::waitKey(-1); #endif if(!image.data) { cout<<"no image data"<<endl; system("pause"); } generate->InsertImage(image); } } #ifdef _DEBUG system("pause"); #endif delete generate; } }
} } return 0;
} 2. 处理生成步态能量图的类
头文件: #pragma once
#include <iostream>
#include <opencv2\core\core.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <opencv2\highgui\highgui.hpp>
#include <vector>
using namespace cv; using namespace std;
typedef std::vector<Point> Contours;
class GenerateGEI { public: int num; //当前计数 explicit GenerateGEI(const char* folderpath); ~GenerateGEI(void); int InsertImage(Mat image); int Process(Mat image); Mat FindContours(Mat image); Mat Resize(Mat image); Mat CalGei(Mat image); private: char* gei_name; Mat gei; //当前gei图
}; 源文件: #include "StdAfx.h" #include "GenerateGEI.h"
#define H 500 #define W 400 #define BASE_RATIO 1.25
GenerateGEI::GenerateGEI(const char* folderpath) { gei_name=new char[100]; sprintf(gei_name,"%s\gei_.jpg",folderpath); //产生的GEI储存位置 num=0; }
GenerateGEI::~GenerateGEI(void) { delete gei_name; }
int GenerateGEI::InsertImage(Mat image) { num++; Process(image); return 1; }
int GenerateGEI::Process(Mat image) { Mat origin=FindContours(image); Mat normal=Resize(origin); Mat show_gei=CalGei(normal); imwrite(gei_name,show_gei); return 1; }
Mat GenerateGEI::FindContours(Mat image) { Mat image_tmp=image.clone(); //用于findContours,因为该函数使用后会导致原图改变,所以要复制一个用于该函数 #ifdef _DEBUG cv::imshow("image",image); cv::waitKey(-1); #endif std::vector<Contours> contours; //每个轮廓的所有角点理论上只需要contours[0] cv::findContours(image_tmp,contours,CV_RETR_CCOMP,CV_CHAIN_APPROX_NONE); //findContours只能处理8UC1和32SC1 cv::Rect r0 = cv::boundingRect(cv::Mat(contours[0])); cv::Mat gait_img_roi = image(r0); return gait_img_roi; }
Mat GenerateGEI::Resize(Mat image) { float ra = image.rows/image.cols; cv::Mat gait_roi_tmp; Mat gait_img2 = Mat::zeros(H,W,CV_8UC1); //if(ra>=base_ratio) { double resize_scale = (double)H/image.rows; cv::resize(image,gait_roi_tmp,cv::Size(),resize_scale,resize_scale); //按比例缩放 for(int i =0;i<gait_roi_tmp.rows;i++) { uchar* p_tmp = gait_roi_tmp.ptr<uchar>(i); uchar* p = gait_img2.ptr<uchar>(i); for(int j=(W-gait_roi_tmp.cols)/2,k=0;k<gait_roi_tmp.cols;k++,j++) { p[j] = p_tmp[k]; } } } //else //{ // double resize_scale = (double)W/image.cols; // cv::resize(image,gait_roi_tmp,cv::Size(),resize_scale,resize_scale); //按比例缩放 // int i =(H-gait_roi_tmp.rows)/2; // for(int k = 0;k<gait_roi_tmp.rows;k++,i++) // { // uchar* p_tmp = gait_roi_tmp.ptr<uchar>(k); // uchar* p = gait_img2.ptr<uchar>(i); // for(int j=0;j<gait_roi_tmp.cols;j++) // { // p[j]=p_tmp[j]; // } // } //} return gait_img2; }
Mat GenerateGEI::CalGei(Mat image) { if(num==1) { gei=image.clone(); } else { //新的图像与原来的gei形成新的gei for(int i=0;i<image.rows;i++) { uchar* p_old_gei=gei.ptr<uchar>(i); uchar* p=image.ptr<uchar>(i); for(int j=0;j<image.cols;j++) { p_old_gei[j]=(p_old_gei[j]*(num-1)+p[j])/num; } } } return gei; } 结果
结果是怎么样的图估计大家都知道,就不在这里啰嗦了,如果你不知道,那估计这篇文章对你也没什么用。
最费劲的是,足足跑了好几个小时,中途要注意findContours这个函数只能结构单通道的输入,要想直接获得单通道的图形矩阵,Mat(src,0)中的第二个参数就是控制图形矩阵通道数目的。
最后还有个很误导人的发现,就是使用imread函数时,然后想用imshow看是否读取成功,一旦设置了断点在imshow后面,那一般情况是看不到图像的,重点是src.data也是显示为0,这时候,不要以为你没有读取成功,我就是以为没有读取成功,然后还以为配置有问题(但我用了OpenCV都一段时间了,虽然平时大多使用视频直接获得图像,但也不至于配置有问题吧~),最后只要在imshow后面设置一个waitkey(0),就可以看到图像了,即使不用waitkey继续运行程序也是没有问题的,图像数据是会有的。
|