一、简介 目标检测方法最初由Paul Viola [Viola01]提出,并由Rainer Lienhart [Lienhart02]对这一方法进行了改善。该方法的基本步骤为: 首先,利用样本(大约几百幅样本图片)的 harr 特征进行分类器训练,得到一个级联的boosted分类器。 分类器中的"级联"是指最终的分类器是由几个简单分类器级联组成。在图像检测中,被检窗口依次通过每一级分类器, 这样在前面几层的检测中大部分的候选区域就被排除了,全部通过每一级分类器检测的区域即为目标区域。 分类器训练完以后,就可以应用于输入图像中的感兴趣区域(与训练样本相同的尺寸)的检测。检测到目标区域(汽车或人脸)分类器输出为1,否则输出为0。为了检测整副图像,可以在图像中移动搜索窗口,检测每一个位置来确定可能的目标。为了搜索不同大小的目标物体,分类器被设计为可以进行尺寸改变,这样比改变待检图像的尺寸大小更为有效。所以,为了在图像中检测未知大小的目标物体,扫描程序通常需要用不同比例大小的搜索窗口对图片进行几次扫描。 目前支持这种分类器的boosting技术有四种: Discrete Adaboost, Real Adaboost, Gentle Adaboost and Logitboost。 "boosted" 即指级联分类器的每一层都可以从中选取一个boosting算法(权重投票),并利用基础分类器的自我训练得到。 根据上面的分析,目标检测分为三个步骤: 1、 样本的创建 2、 训练分类器 3、 利用训练好的分类器进行目标检测。 二、样本创建 训练样本分为正例样本和反例样本,其中正例样本是指待检目标样本(例如人脸或汽车等),反例样本指其它任意图片,所有的样本图片都被归一化为同样的尺寸大小(例如,20x20)。 负样本 负样本可以来自于任意的图片,但这些图片不能包含目标特征。负样本由背景描述文件来描述。背景描述文件是一个文本文件,每一行包含了一个负样本图片的文件名(基于描述文件的相对路径)。该文件必须手工创建。 e.g: 负样本描述文件的一个例子: 假定目录结构如下: /img img1.jpg img2.jpg bg.txt 则背景描述文件bg.txt的内容为: img/img1.jpg img/img2.jpg 正样本 正样本由程序craatesample程序来创建。该程序的源代码由OpenCV给出,并且在bin目录下包含了这个可执行的程序。 正样本可以由单个的目标图片或者一系列的事先标记好的图片来创建。 Createsamples程序的命令行参数: 命令行参数: -vec <vec_file_name> 训练好的正样本的输出文件名。 -img<image_file_name> 源目标图片(例如:一个公司图标) -bg<background_file_name> 背景描述文件。 -num<number_of_samples> 要产生的正样本的数量,和正样本图片数目相同。 -bgcolor<background_color> 背景色(假定当前图片为灰度图)。背景色制定了透明色。对于压缩图片,颜色方差量由bgthresh参数来指定。则在bgcolor-bgthresh和bgcolor+bgthresh中间的像素被认为是透明的。 -bgthresh<background_color_threshold> -inv 如果指定,颜色会反色 -randinv 如果指定,颜色会任意反色 -maxidev<max_intensity_deviation> 背景色最大的偏离度。 -maxangel<max_x_rotation_angle> -maxangle<max_y_rotation_angle>, -maxzangle<max_x_rotation_angle> 最大旋转角度,以弧度为单位。 -show 如果指定,每个样本会被显示出来,按下"esc"会关闭这一开关,即不显示样本图片,而创建过程继续。这是个有用的debug选项。 -w<sample_width> 输出样本的宽度(以像素为单位) -h《sample_height》 输出样本的高度,以像素为单位。 注:正样本也可以从一个预先标记好的图像集合中获取。这个集合由一个文本文件来描述,类似于背景描述文件。每一个文本行对应一个图片。每行的第一个元素是图片文件名,第二个元素是对象实体的个数。后面紧跟着的是与之匹配的矩形框(x, y, 宽度,高度)。 下面是一个创建样本的例子: 假定我们要进行人脸的检测,有5个正样本图片文件img1.bmp,…img5.bmp;有2个背景图片文件:bg1.bmp,bg2.bmp,文件目录结构如下: positive img1.bmp …… Img5.bmp negative bg1.bmp bg2.bmp info.dat bg.txt 正样本描述文件info.dat的内容如下: Positive/imag1.bmp 1 0 0 24 28 …… Positive/imag5.bmp 1 0 0 24 28 图片img1.bmp包含了单个目标对象实体,矩形为(0,0,24,28)。 注意:要从图片集中创建正样本,要用-info参数而不是用-img参数。 -info <collect_file_name> 标记特征的图片集合的描述文件。 背景(负样本)描述文件的内容如下: nagative/bg1.bmp nagative/bg2.bmp 我们用一个批处理文件run.bat来进行正样本的创建:该文件的内容如下: cd e:\face\bin CreateSamples -vec e:\face\a.vec -info e:\face\info.dat -bg e:\face\bg.txt -num 5 -show -w 24 -h 28 其中e:\face\bin目录包含了createsamples可执行程序,生成的正样本文件a.vec在e:\face目录下。 三、训练分类器 样本创建之后,接下来要训练分类器,这个过程是由haartraining程序来实现的。该程序源码由OpenCV自带,且可执行程序在OpenCV安装目录的bin目录下。 Haartraining的命令行参数如下: -data<dir_name> 存放训练好的分类器的路径名。 -vec<vec_file_name> 正样本文件名(由trainingssamples程序或者由其他的方法创建的) -bg<background_file_name> 背景描述文件。 -npos<number_of_positive_samples>, -nneg<number_of_negative_samples> 用来训练每一个分类器阶段的正/负样本。合理的值是:nPos = 7000;nNeg = 3000 -nstages<number_of_stages> 训练的阶段数。 -nsplits<number_of_splits> 决定用于阶段分类器的弱分类器。如果1,则一个简单的stump classifier被使用。如果是2或者更多,则带有number_of_splits个内部节点的CART分类器被使用。 -mem<memory_in_MB> 预先计算的以MB为单位的可用内存。内存越大则训练的速度越快。 -sym(default) -nonsym 指定训练的目标对象是否垂直对称。垂直对称提高目标的训练速度。例如,正面部是垂直对称的。 -minhitrate《min_hit_rate》 每个阶段分类器需要的最小的命中率。总的命中率为min_hit_rate的number_of_stages次方。 -maxfalsealarm<max_false_alarm_rate> 没有阶段分类器的最大错误报警率。总的错误警告率为max_false_alarm_rate的number_of_stages次方。 -weighttrimming<weight_trimming> 指定是否使用权修正和使用多大的权修正。一个基本的选择是0.9 -eqw -mode<basic(default)|core|all> 选择用来训练的haar特征集的种类。basic仅仅使用垂直特征。all使用垂直和45度角旋转特征。 -w《sample_width》 -h《sample_height》 训练样本的尺寸,(以像素为单位)。必须和训练样本创建的尺寸相同。 一个训练分类器的例子: 同上例,分类器训练的过程用一个批处理文件run2.bat来完成: cd e:\face\bin haartraining -data e:\face\data -vec e:\face\a.vec -bg e:\face\bg.txt -npos 5 -nneg 2 -w 24 -h 28 训练结束后,会在目录data下生成一些子目录,即为训练好的分类器。 注:OpenCv 的某些版本可以将这些目录中的分类器直接转换成xml文件。但在实际的操作中,haartraining程序却好像永远不会停止,而且没有生成xml文件,后来在OpenCV的yahoo论坛上找到一个haarconv的程序,才将分类器转换为xml文件,其中的原因尚待研究。 Haar分类器,ObjectMarker程序源代码 /** * Modifications to be made: * 1. Two possible modes: free-size windowing and fixed-scale windowing * !!DONE!! 2. Add and remove the newly added window * !!DONE!! 3. Expand the width or height of the window by a constant number of pixels * !!DONE!! 4. Expand the dimension (both width and height) of window by a constant scale (e.g. 0.1) * !!DONE!! 5. Move the window to left, right, up, and down */ #include "stdafx.h" #ifdef WIN32 //#include "stdafx.h" #include <io.h> #else #include <sys/types.h> #include <dirent.h> #endif #include "cv.h" #include "cvaux.h" #include "highgui.h" #include <stdio.h> #include <time.h> #include <string> #include <fstream> #include <sstream> #include <vector> using namespace std; struct DPrecRect { int x; int y; double width; double height; }; IplImage* image = 0; IplImage* image2 = 0; double roi_x0 = -1; // (roi_x0, roi_y0) is the coordinate of first corner double roi_y0 = -1; double roi_x1 = -1; // (roi_x1, roi_y1) is the coordinate of second corner double roi_y1 = -1; float scale_width = 5; float scale_height = 5; vector<DPrecRect> objects; char* input_dir = "rawdata"; char* outputname = "annotation.txt"; char* output_dir = "cropped"; char* raw_image_ext = ".jpg"; char window_name[200] = "Object Marker"; bool FIXED_SCALE = true; bool ACCEPT_POINT = false; void printHelp(); void redrawImage(); void on_mouse(int, int, int, int, void*); void saveObjects(FILE*, const char*); int cropImages(const char*); void printHelp() { printf("\nObject Marker: \n\ta tool to annotate the locations of objects in an image\n"); printf("\tGunawan Herman, April 2006\n"); printf("\tAdapted from ObjectMarker.cpp by A.Florian\n"); printf("\n"); printf("\tThis program searches for input images in dir '%s'\n", input_dir); printf("\tLocations of objects will be printed out to file '%s'\n", outputname); printf("\n"); printf("------------------------------------------------------------\n"); printf("| btn | function |\n"); printf("|-------|--------------------------------------------------|\n"); printf("|Enter | write currently marked objects to file |\n"); printf("| | and proceed to the next image |\n"); printf("|Space | same |\n"); printf("|ESC | close this program |\n"); printf("|d | delete the most recently added rect |\n"); printf("|8 | move recently added rect up by 1 px |\n"); printf("|9 | move recently added rect up by 10 pxs |\n"); printf("|2 | move recently added rect down by 1 px |\n"); printf("|3 | move recently added rect down by 10 pxs |\n"); printf("|4 | move recently added rect left by 1 px |\n"); printf("|5 | move recently added rect left by 10 pxs |\n"); printf("|6 | move recently added rect right by 1 px |\n"); printf("|7 | move recently added rect right by 10 pxs |\n"); printf("|w | enlarge width of recently added rect by 1 px |\n"); printf("|W | reduce width of recently added rect by 1 px |\n"); printf("|h | enlarge height of recently added rect by 1 px |\n"); printf("|H | reduce height of recently added rect by 1 px |\n"); printf("|z | zoom in recently added rect by 2%% |\n"); printf("|Z | zoom out recently added rect by 2%% |\n"); printf("|m | switch between free-size and fixed-scale mode & |\n"); printf("| | to lock-in the width-height ratio |\n"); printf("|s | input the scale constant |\n"); printf("|p | objects can be marked with rect as well as with |\n"); printf("| | points instead of rect |\n"); printf("|t | print this instruction |\n"); printf("|j | jump to image whose index is specified |\n"); printf("------------------------------------------------------------\n"); printf("\n"); printf("mode = %s\n", FIXED_SCALE ? "FIXED_SCALE" : "FREE_SIZE"); if(FIXED_SCALE) { printf("Scale constant: %f x %f\n", scale_width, scale_height); } printf("ACCEPT_POINT = %s\n", ACCEPT_POINT ? "YES" : "NO"); } void redrawImage() { //printf("redrawImage()\n"); image2 = cvCloneImage(image); // Display all rectangles int numOfRect = (int)(objects.size()); for(int i = 0; i < numOfRect; i++) { DPrecRect rectangle = objects.at(i); if(rectangle.width > 0 || rectangle.height > 0) { cvRectangle(image2, cvPoint(rectangle.x, rectangle.y), cvPoint(rectangle.x + (int)(rectangle.width), rectangle.y + (int)(rectangle.height)), CV_RGB(255, 0, 0), 1); } else { cvCircle(image2, cvPoint(rectangle.x, rectangle.y), 1, CV_RGB(255, 0, 0)); } } if(roi_x0 > 0) { cvRectangle(image2, cvPoint((int)roi_x0,(int)roi_y0), cvPoint((int)roi_x1,(int)roi_y1), CV_RGB(255,0,0),1); } cvShowImage(window_name, image2); cvReleaseImage(&image2); } void on_mouse(int event,int x,int y,int flag, void*) { // If left mouse button is pressed, record the first coordinate if(event == CV_EVENT_LBUTTONDOWN) { //printf("Left mouse button pressed\n"); roi_x0 = roi_x1 = x; roi_y0 = roi_y1 = y; } // If mouse is moved while left mouse button is still pressed #ifdef WIN32 if(event == CV_EVENT_MOUSEMOVE && flag == CV_EVENT_FLAG_LBUTTON) { #else if(event == CV_EVENT_MOUSEMOVE && flag == 33) { #endif //printf("Move moved with left button pressed\n"); roi_x1 = x; if(!FIXED_SCALE) { roi_y1 = y; } else { roi_y1 = roi_y0 + (((roi_x1 - roi_x0)/scale_width) * scale_height); } redrawImage(); } // If left mouse button is released if(event == CV_EVENT_LBUTTONUP) { //printf("Left button released\n"); int topleft_x = min((int)roi_x0, (int)roi_x1); int topleft_y = min((int)roi_y0, (int)roi_y1); double width = abs((int)roi_x0 - (int)roi_x1); double height = abs((int)roi_y0 - (int)roi_y1); if((width == 0 || height == 0) && !ACCEPT_POINT) return; DPrecRect rectangle = {topleft_x, topleft_y, width, height}; objects.push_back(rectangle); roi_x0 = -1; // indicates that there's no temporary rectangle redrawImage(); } } void saveObjects(FILE* output, const char* imagename) { int numOfRect = (int)(objects.size()); if(numOfRect) { fprintf(output, "%s %d ", imagename, objects.size()); for(int i = 0; i < (int)(objects.size()); i++) { DPrecRect rectangle = objects.at(i); fprintf(output, "%d %d %d %d ", rectangle.x, rectangle.y, (int)(rectangle.width), (int)(rectangle.height)); } fprintf(output, "\n"); fflush(output); } } int cropImages(const char* datafile) { static int index = 1; int counts = 0; FILE* in = fopen(datafile, "r"); if(!in) { printf("Error: cannot open %s to read\n", datafile); exit(-1); } while(true) { char imagename[150], crop_output[20]; int nrects, x, y, w, h; if(fscanf(in, "%s %d", imagename, &nrects) < 2) break; printf("%s %d\n", imagename, nrects); IplImage* tmpimage = cvLoadImage(imagename, 0); IplImage* scaled_size = cvCloneImage(tmpimage); scaled_size->width = 24; //24;//16; scaled_size->height = 24; //12;//8; if(!tmpimage) { printf("Error: cannot load %s\n", imagename); exit(-1); } for(int i = 0; i < nrects; i++) { if(fscanf(in, "%d %d %d %d", &x, &y, &w, &h) < 4) { printf("%s %d %d\n", imagename, i, nrects); printf("Invalid structure in %s\n", datafile); exit(-1); } struct _IplROI roi = {0, x, y, w, h}; tmpimage->roi = &roi; sprintf(crop_output, "%s/%05d.jpg", output_dir, index++); cvSaveImage(crop_output, tmpimage); // Load the object, and scale it to desired size IplImage* original_size = cvLoadImage(crop_output, 0); cvResize(original_size, scaled_size); // Overwrite the original object with scaled object cvSaveImage(crop_output, scaled_size); cvReleaseImage(&original_size); counts++; if(counts % 50 == 0) { printf("*"); } } tmpimage->roi = NULL; cvReleaseImage(&tmpimage); cvReleaseImage(&scaled_size); } printf("\n"); fclose(in); return counts; } #ifdef WIN32 int _tmain(int argc, _TCHAR* argv[]) { if(argc > 1) { int n = cropImages(outputname); printf("%d images has been produced", n); if(n > 0) { printf(": %s/%05d.jpg - %s/%05d.jpg", output_dir, 1, output_dir, n); } printf("\n"); exit(0); } for(int i = 0; i < argc; i++) { printf("%s\n", argv[i]); } printHelp(); enum KeyBindings { Key_Enter = 13, Key_ESC = 27, Key_Space = 32, Key_d = 100, Key_8 = 56, Key_9 = 57, Key_2 = 50, Key_3 = 51, Key_4 = 52, Key_5 = 53, Key_6 = 54, Key_7 = 55, Key_w = 119, Key_W = 87, Key_h = 104, Key_H = 72, Key_z = 122, Key_Z = 90, Key_m = 109, Key_s = 115, Key_e = 101, Key_p = 112, Key_t = 116, Key_j = 106 }; struct _finddata_t bmp_file; long hFile; int iKey = 0; FILE* output = fopen(outputname, "a"); if(!output) { printf("Error: cannot open %s to write to\n", outputname); exit(-1); } time_t rawtime; struct tm * timeinfo; time(&rawtime); timeinfo = localtime(&rawtime); fprintf(output, "\n"); fprintf(output, "########################\n"); fprintf(output, " %s ", asctime(timeinfo)); fprintf(output, "########################\n"); int targetImageIndex = 1; static int counter = 1; // get *.bmp files in directory char pattern[150]; sprintf(pattern, "%s/*.jpg", input_dir); if((hFile = (long)_findfirst(pattern, &bmp_file)) ==-1L) { printf("no appropriate input files in directory 'rawdata'\n"); } else { // init highgui cvAddSearchPath(input_dir); string strPrefix; // open every *.bmp file do { if(counter != targetImageIndex) { counter++; continue; } objects.clear(); strPrefix = string(input_dir) + string("/"); strPrefix += bmp_file.name; image = cvLoadImage(strPrefix.c_str(), 1); //window_name = bmp_file.name; sprintf(window_name, "%d - %s", counter, bmp_file.name); cvNamedWindow(window_name, 1); cvSetMouseCallback(window_name, on_mouse); cvShowImage(window_name, image); bool cont = false; do { // Get user input iKey = cvWaitKey(0); switch(iKey) { // Press ESC to close this program, any unsaved changes will be discarded case Key_ESC: cvReleaseImage(&image); cvDestroyWindow(window_name); fclose(output); return 0; // Press Space or Enter to save marked objects on current image and proceed to the next image case Key_Space: case Key_Enter: cont = false; saveObjects(output, strPrefix.c_str()); break; // Press d to remove the last added object case Key_d: cont = true; objects.pop_back(); redrawImage(); break; case Key_8: case Key_9: cont = true; if(!objects.empty()) { objects.back().y -= (iKey == Key_8) ? 1 : 10; redrawImage(); } break; case Key_2: case Key_3: cont = true; if(!objects.empty()) { objects.back().y += (iKey == Key_2) ? 1 : 10; redrawImage(); } break; case Key_4: case Key_5: cont = true; if(!objects.empty()) { objects.back().x -= (iKey == Key_4) ? 1 : 10; redrawImage(); } break; case Key_6: case Key_7: cont = true; if(!objects.empty()) { objects.back().x += (iKey == Key_6) ? 1 : 10; redrawImage(); } break; case Key_w: case Key_W: cont = true; if(!objects.empty()) { objects.back().width += (iKey == Key_w) ? 1 : ((objects.back().width > 5) ? -1 : 0); redrawImage(); } break; case Key_h: case Key_H: cont = true; if(!objects.empty()) { objects.back().height += (iKey == Key_h) ? 1 : ((objects.back().height > 5) ? -1 : 0); redrawImage(); } break; case Key_z: case Key_Z: cont = true; if(!objects.empty()) { objects.back().width *= (iKey == Key_z) ? 1.02 : ((objects.back().width > 5 && objects.back().height > 5) ? 0.98 : 1); objects.back().height *= (iKey == Key_z) ? 1.02 : ((objects.back().width > 5 && objects.back().height > 5) ? 0.98 : 1); redrawImage(); } break; case Key_m: cont = true; FIXED_SCALE = !FIXED_SCALE; printf("mode = %s\n", FIXED_SCALE ? "FIXED_SCALE" : "FREE_SIZE"); if(FIXED_SCALE) { if(!objects.empty()) { scale_width = objects.back().width; scale_height = objects.back().height; } else { scale_width = scale_height = 5; } printf("Scale constant: %f x %f\n", scale_width, scale_height); } break; case Key_s: cont = true; printf("Input the scale constant:\n"); printf("\twidth : "); scanf("%f", &scale_width); printf("\theight: "); scanf("%f", &scale_height); printf("Scale constant: %f x %f\n", scale_width, scale_height); break; case Key_e: cont = true; for(int i = 0; i < (int)(objects.size()); i++) { printf("%d %d %f %f\n", objects.at(i).x, objects.at(i).y, objects.at(i).width, objects.at(i).height); } break; case Key_p: cont = true; ACCEPT_POINT = !ACCEPT_POINT; printf("ACCEPT_POINT = %s\n", ACCEPT_POINT ? "YES" : "NO"); break; case Key_t: cont = true; printHelp(); break; case Key_j: cont = false; printf("Jump to image#: "); scanf("%d", &targetImageIndex); targetImageIndex--; // because targetImageIndex++ below break; default: if(iKey >= 0 && iKey <= 127) { cont = true; printf("Unrecognised command\n"); } else { cont = false; } } } while(cont); counter++; targetImageIndex++; cvDestroyWindow(window_name); cvReleaseImage(&image); } while(_findnext(hFile,&bmp_file)==0); fclose(output); _findclose( hFile ); } return 0; } #else int main(int argc, char* argv[]) { if(argc > 1) { int n = cropImages(outputname); printf("%d images has been produced", n); if(n > 0) { printf(": %s/%05d.jpg - %s/%05d.jpg", output_dir, 1, output_dir, n); } printf("\n"); exit(0); } for(int i = 0; i < argc; i++) { printf("%s\n", argv[i]); } printHelp(); enum KeyBindings { Key_Enter = 1048586, Key_ESC = 1048603, Key_Space = 1048608, Key_d = 1048676, Key_8a = 1114040, Key_8b = 1048632, Key_9a = 1048633, Key_9b = 1114041, Key_2a = 1048626, Key_2b = 1114034, Key_3a = 1048627, Key_3b = 1114035, Key_4a = 1048628, Key_4b = 1114036, Key_5a = 1048629, Key_5b = 1114037, Key_6a = 1048630, Key_6b = 1114038, Key_7a = 1048631, Key_7b = 1114039, Key_w = 1048695, Key_W = 1114199, Key_h = 1048680, Key_H = 1114184, Key_z = 1048698, Key_Z = 1114202, Key_m = 1048685, Key_s = 1048691, Key_e = 1048677, Key_p = 1048688, Key_t = 1048692, Key_j = 1048682 }; int iKey = 0; FILE* output = fopen(outputname, "a"); if(!output) { printf("Error: cannot open %s to write to\n", outputname); exit(-1); } time_t rawtime; struct tm * timeinfo; time(&rawtime); timeinfo = localtime(&rawtime); fprintf(output, "\n"); fprintf(output, "########################\n"); fprintf(output, " %s ", asctime(timeinfo)); fprintf(output, "########################\n"); int targetImageIndex = 1; static int counter = 1; // get *.bmp files in directory DIR* dir = opendir(input_dir); struct dirent* dp; // init highgui cvAddSearchPath(input_dir); string strPrefix; // open every *.bmp file while(dir) { if(counter != targetImageIndex) { counter++; continue; } if(dp = readdir(dir)) { if(strstr(dp->d_name, raw_image_ext)) { //printf("%s\n", dp->d_name); } else { continue; } } else { closedir(dir); break; } objects.clear(); strPrefix = string(input_dir) + string("/"); strPrefix += dp->d_name; image = cvLoadImage(strPrefix.c_str(), 1); sprintf(window_name, "%d - %s", counter, dp->d_name); cvNamedWindow(window_name, 1); cvSetMouseCallback(window_name, on_mouse); cvShowImage(window_name, image); bool cont = false; do { // Get user input iKey = cvWaitKey(0); switch(iKey) { // Press ESC to close this program, any unsaved changes will be discarded case Key_ESC: //printf("ESC is pressed\n"); cvReleaseImage(&image); cvDestroyWindow(window_name); fclose(output); return 0; // Press Space or Enter to save marked objects on current image and proceed to the next image case Key_Space: case Key_Enter: //printf("Enter or Space is pressed\n"); cont = false; saveObjects(output, strPrefix.c_str()); break; // Press d to remove the last added object case Key_d: //printf("d is pressed\n"); cont = true; objects.pop_back(); redrawImage(); break; case Key_8a: case Key_8b: case Key_9a: case Key_9b: //printf("Key 8 or 9 is pressed\n"); cont = true; if(!objects.empty()) { objects.back().y -= (iKey == Key_8a || iKey == Key_8b) ? 1 : 10; redrawImage(); } break; case Key_2a: case Key_2b: case Key_3a: case Key_3b: //printf("Key 2 or 3 is pressed\n"); cont = true; if(!objects.empty()) { objects.back().y += (iKey == Key_2a || iKey == Key_2b) ? 1 : 10; redrawImage(); } break; case Key_4a: case Key_4b: case Key_5a: case Key_5b: //printf("Key 4 or 5 is pressed\n"); cont = true; if(!objects.empty()) { objects.back().x -= (iKey == Key_4a || iKey == Key_4b) ? 1 : 10; redrawImage(); } break; case Key_6a: case Key_6b: case Key_7a: case Key_7b: //printf("Key 6 or 7 is pressed\n"); cont = true; if(!objects.empty()) { objects.back().x += (iKey == Key_6a || iKey == Key_6b) ? 1 : 10; redrawImage(); } break; case Key_w: case Key_W: //printf("Key w or W is pressed\n"); cont = true; if(!objects.empty()) { objects.back().width += (iKey == Key_w) ? 1 : ((objects.back().width > 5) ? -1 : 0); redrawImage(); } break; case Key_h: case Key_H: //printf("Key h or H is pressed\n"); cont = true; if(!objects.empty()) { objects.back().height += (iKey == Key_h) ? 1 : ((objects.back().height > 5) ? -1 : 0); redrawImage(); } break; case Key_z: case Key_Z: //printf("Key z or Z is pressed\n"); cont = true; if(!objects.empty()) { objects.back().width *= (iKey == Key_z) ? 1.02 : ((objects.back().width > 5 && objects.back().height > 5) ? 0.98 : 1); objects.back().height *= (iKey == Key_z) ? 1.02 : ((objects.back().width > 5 && objects.back().height > 5) ? 0.98 : 1); redrawImage(); } break; case Key_m: //printf("Key m is pressed\n"); cont = true; FIXED_SCALE = !FIXED_SCALE; printf("mode = %s\n", FIXED_SCALE ? "FIXED_SCALE" : "FREE_SIZE"); if(FIXED_SCALE) { if(!objects.empty()) { scale_width = objects.back().width; scale_height = objects.back().height; } else { scale_width = scale_height = 5; } printf("Scale constant: %f x %f\n", scale_width, scale_height); } break; case Key_s: //printf("Key s is pressed\n"); cont = true; printf("Input the scale constant:\n"); printf("\twidth : "); scanf("%f", &scale_width); printf("\theight: "); scanf("%f", &scale_height); printf("Scale constant: %f x %f\n", scale_width, scale_height); break; case Key_e: //printf("Key e is pressed\n"); cont = true; for(int i = 0; i < (int)(objects.size()); i++) { printf("%d %d %f %f\n", objects.at(i).x, objects.at(i).y, objects.at(i).width, objects.at(i).height); } break; case Key_p: //printf("Key p is pressed\n"); cont = true; ACCEPT_POINT = !ACCEPT_POINT; printf("ACCEPT_POINT = %s\n", ACCEPT_POINT ? "YES" : "NO"); break; case Key_t: //printf("Key t is pressed\n"); cont = true; printHelp(); break; case Key_j: //printf("Key j is pressed\n"); cont = false; printf("Jump to image#: "); scanf("%d", &targetImageIndex); targetImageIndex--; // because targetImageIndex++ below break; default: if(iKey >= 0) { printf("Unrecognised command\n"); cont = true; } else { cvReleaseImage(&image); cvDestroyWindow(window_name); fclose(output); return 0; } } } while(cont); counter++; targetImageIndex++; cvDestroyWindow(window_name); cvReleaseImage(&image); } fclose(output); return 0; } #endif |