为了方便在制作机器学习所需的样本数据,提高工作效率,写了一个简单的矩形框标定并保存标定图像的程序,相关程序可由VS2012+OpenCV2411生成,默认对352*288大小的视频手动进行目标样本截取并保存。
先切换到程序marksave的目录下,需要在目录下分别建立一个叫avi和obj的文件夹,前者放视频,后者存放标定的样本数据,然后在cmd命令行中输入相关参数。
注意输入参数格式依次如下:
第一个输入参数为程序名:marksave.exe;
第二个输入参数为视频名:avi/test.avi为视频文件,放置在当前目录下的avi文件夹里;
第三个输入参数控制图像输出格式:1表示会将所截取的图修正为正方形,若取0则不作修正;
第四个输入参数表示起始帧数:10表示从第10帧开始播放;
第五个输入参数表示图片保存格式:0表示存储为*.png,1表示存储为*.jpg。
程序开始执行后,按键P可以控制视频的暂停/播放;暂停后用鼠标先后点击目标的左上角和有下角确定需要的保存区域,逐个标定完后,再按P继续播放。结果保存在obj文件夹下,第三个输入参数为1时在保存时会对图像比例做修正,使之为正方形。
代码如下,大家可以根据自己的需求做下修改应用到自己的项目上。
// marksave.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
#include <math.h>
#include <time.h>
#include <iostream>
#include <fstream>
#include <Windows.h>
#define OBJLEN 10
//视频图像宽高
#define IMGWIDHT 352
#define IMGHEIGHT 288
using namespace cv;
using namespace std;
int objnum = 0;
bool pause = false;//是否暂停
IplImage *curframe = NULL;
//复制原图像
IplImage *localframe = cvCreateImage(cvSize(IMGWIDHT,IMGHEIGHT),IPL_DEPTH_8U, 3);
bool lineflag = true;
//ROI区域两端点坐标
int pointl_x[OBJLEN] = {0};
int pointl_y[OBJLEN] = {0};
int pointr_x[OBJLEN] = {0};
int pointr_y[OBJLEN] = {0};
//鼠标点击获取坐标信息
void on_mouse( int event, int x, int y, int flags, void* ustc)
{
CvFont font;
cvInitFont(&font, CV_FONT_HERSHEY_SIMPLEX, 0.5, 0.5, 0, 1, CV_AA);
if( event == CV_EVENT_LBUTTONDOWN )
{
if(lineflag)
{
pointl_x[objnum] = x;
pointl_y[objnum] = y;
}
else
{
pointr_x[objnum] = x;
pointr_y[objnum] = y;
objnum++;
}
CvPoint pt = cvPoint(x,y);
#if 0
char temp[16];
if(lineflag)
sprintf(temp,"L(%d,%d)",pt.x,pt.y); //显示鼠标点击的左上角坐标
else
sprintf(temp,"R(%d,%d)",pt.x,pt.y); //显示鼠标点击的右上角坐标
cvPutText(curframe,temp, pt, &font, cvScalar(255, 255, 255));
#endif
cvCircle( curframe, pt, 2, cvScalar(255,0,0),CV_FILLED, CV_AA, 0 );
cvLine(curframe,cvPoint(pt.x,1),cvPoint(pt.x,IMGHEIGHT-1),Scalar(0,255,0),1);
cvLine(curframe,cvPoint(1,pt.y),cvPoint(IMGWIDHT-1,pt.y),Scalar(0,255,0),1);
cvShowImage( "video", curframe );
if(!lineflag)
cvCopy(localframe,curframe,NULL);
lineflag = !lineflag;
}
}
void main(int argc, char* argv[])
{
int frames = 0;
int FrameNum = 0;//帧号
int k = 0;
int idx;
int ROIx;
int ROIy;
int ROIwidth;
int ROIheight;
//CvCapture *capture = cvCreateFileCapture("test.avi");
CvCapture *capture = cvCreateFileCapture(argv[1]);
cout<<"视频读取成功!"<<endl;
int rectflag = stoi(argv[2]);
int startframe = stoi(argv[3]);
int imgformat = stoi(argv[4]);
frames = cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_COUNT);//获取视频帧数
printf("total frames is %d\n", frames);
cout<<"按键P可以控制视频的暂停/播放"<<endl;
cout<<"暂停后用鼠标先后点击目标的左上角和有下角确定需要的保存区域"<<endl;
uchar key = false;//用来设置暂停
cvNamedWindow("video",0);
//滑鼠事件呼叫函式
cvSetMouseCallback( "video", on_mouse, 0 );
while (capture)
{
curframe = cvQueryFrame(capture); //抓取一帧
FrameNum++;
k++;
if(FrameNum>startframe)
{
cout<<"frame:"<<FrameNum-1<<endl;
Mat img(curframe,0);//0是不复制影像,也就是iplImg的data共用同个记意位置,header各自有
cvCopy(curframe,localframe,NULL);
cvShowImage("video",curframe);//显示当前帧
#if 0
if(0 == FrameNum%3)//每隔一定帧数暂停一次用鼠标标定ROI区域
{
pause = true;
}
#endif
//按键P切换暂停和播放
key = cvWaitKey(1);
if(key == 'p') pause = true;
while(pause)
if(cvWaitKey(0)=='p')
pause = false;
//暂停标定好区域后保存选定区域
if(1)
{
for(idx=0;idx<objnum;idx++)
{
char resultImgName[256];
ROIx = pointl_x[idx];
ROIy = pointl_y[idx];
if(rectflag>=1)
{
ROIwidth = max(pointr_x[idx]-ROIx+1,pointr_y[idx]-ROIy+1);
ROIheight = ROIwidth;
}
else
{
ROIwidth = pointr_x[idx]-ROIx+1;
ROIheight = pointr_y[idx]-ROIy+1;
}
cvSetImageROI(localframe,cvRect(ROIx, ROIy, ROIwidth, ROIheight));//设置源图像ROI
if(imgformat>0)
sprintf(resultImgName, "obj/%d_%d.jpg", k,idx);
else
sprintf(resultImgName, "obj/%d_%d.png", k,idx);
cvSaveImage( resultImgName, localframe );
cvResetImageROI(localframe);//源图像用完后,清空ROI
}
}
objnum = 0;
if(FrameNum == frames-2)//截止帧数
break;
}
}
cvWaitKey(0);
cvDestroyWindow("video");
cvReleaseImage(&curframe);
cvReleaseImage(&localframe);
}