最近一直在看论文写报告,在这个过程中想要把论文中的图片某一部分精确的截下来;尤其是在Ubuntu系统下,截图一直靠自己编写小程序实现,所以干脆编一个截图程序一直用。今天在这里分享给大家,给想学习OpenCV或者编写程序练手的同学一个尽量详细的教程。
使用简介:
1. 程序输入:
需要打开的文件名,可以是图片或者视频,输入的是相对路径或者绝对路径。例如程序在桌面上,同时桌面上有一个名为test.mp4的视频。输入的文件名可以是相对路径“test.mp4”或者绝对路径“C:\Users\XXX\Desktop\test.mp4”。程序自动识别输入文件是视频还是图片,无法自动识别时会要求手动输入类型。
2. 程序操作:
按住鼠标拖动调整矩形框,每次点击会激活离点击点最近的一个矩形框顶点,通过按键‘w’‘s’‘a’‘d’四个按键微调这个点的位置;视频输入可以通过滑动条选取想要的一帧图像;点击Esc键确定选取范围或者按‘c’键重新输入新的文件。
3. 使用演示:
由于程序中有参数处理的部分,所以演示时通过命令行启动程序,也可以双击直接启动。
下面是截取的结果:
源代码:
注意:此代码仅在Windows下调试成功
/*************************************************
COPYRIGHT NOTICE
Copyright (c) 2016.10.19, GPH
All rights reserved.
Description: Deal with source pictures or vedios,
cut the wanted part.
*************************************************/
#include <opencv2/opencv.hpp>
#include <string>
#include <time.h>
using namespace cv;
using namespace std;
#define unknown_type 0
#define img_type 1
#define video_type 2
VideoCapture video; //处理视频的类
int totalFrame; //记录视频包含的总帧数
int curFrame = 1, locFrame_global = 1; //与滑动条回调函数相关的变量
Mat img;
string src_type_img[] = { ".jpg", ".png", ".jpeg" }; //保存常见的图片类型,用于自动识别输入类型
string src_type_video[] = { ".avi", ".mp4" }; //保存常见的视频类型,用于自动识别输入类型
//定义变量,记录选取的矩形区域
Point upper_left, upper_right, lower_left, lower_right, *cur_point, p_ul;
int flag_cur_point; //记录当前选中的矩形的顶点
Point cross_u, cross_d, cross_l, cross_r; //用于绘制红色十字光标,显示选中的顶点
int xmax, xmin, ymax, ymin;
clock_t t_rect_delay;
int min_rect_delay = 10;
bool flag_select = false;
void on_Mouse(int event, int x, int y, int flags, void* param); //鼠标响应函数
void on_Bar(int locFrame, void*); //滑动条响应函数
int main(int argc, char **argv)
{
int flag_first_strat = 1; //程序是否是第一次启动
Mat img_draw, img_dst;
int input_type = unknown_type;
string filename;
string filename_type;
// get input filename
get_filename: //按‘c’重新输入文件名使,程序跳转到这里开始执行
//处理程序第一次启动时的输入,argc为输入的个数,argv记录输入的字符串
if (flag_first_strat == 1)
{
if (argc == 1)//只有一个输入,相当于双击启动时,需要输入文件名
{
cout << "Please input the source file:\n";
cin >> filename;
}
else if (argc == 2)//通过命令行启动,同时输入了文件名,则直接获取文件名
{
filename = argv[1];
}
else//输入参数有误,跳转到get_filename
{
cout << "Wrong input!\n";
flag_first_strat = 0;
goto get_filename;
}
}
if (flag_first_strat == 0)//程序不是第一次启动,而是通过按'c'重新选择文件
{
cout << "Input another file name:\n";
cin >> filename;
}
flag_first_strat = 0;
// get the type of the input file
input_type = unknown_type;
int dot_loc;
dot_loc = filename.rfind('.');
if (dot_loc == -1) // input filename contains no dot
{
cout << "Wrong input!\n"; goto get_filename;
}
filename_type = filename.substr(dot_loc, filename.length() - 1); //获得表示文件类型的字段,也就是‘.’以及之后的字符
//判断是什么类型的输入
for (int i = 0, i_len = src_type_img->length(); i < i_len; i++)
{
if (filename_type == src_type_img[i])
{
input_type = img_type;
break;
}
}
if (input_type == unknown_type)
{
for (int i = 0, i_len = src_type_video->length(); i < i_len; i++)
{
if (filename_type == src_type_video[i])
{
input_type = video_type;
break;
}
}
}
if (input_type == unknown_type)//无法自动判断类型,要求用户手动输入类型
{
int img = img_type, video = video_type;
cout << "Fail to read the type of the input file, please input the type of the file\n(" << img << ":img," << video << ":video)\n";
cin >> input_type;
}
//输出程序得到的文件类型
if (input_type == img_type)
cout << "img\n";
else if (input_type == video_type)
cout << "video\n";
else
cout << "unknown\n";
//开始处理
namedWindow("Source img");
setMouseCallback("Source img", on_Mouse);
if (input_type == img_type)
{
img = imread(filename);//如果是图片类型,直接读入图片
}
else if (input_type == video_type)
{
video.open(filename);//视频类型先打开视频
if (!video.isOpened())
{
cout << "Fail to open video!\n"; return 0;
}
video >> img;//获取初始帧
totalFrame = video.get(CV_CAP_PROP_FRAME_COUNT) - 1;//获取视频总帧数
if (totalFrame > 0)
{
createTrackbar("Slide", "Source img", &locFrame_global, totalFrame, on_Bar);//创建滑动条,通过回调函数改变从视频中获取的帧
}
}
Point loc_roi, size_roi;//记录感兴趣区域的点
clock_t t_cross = clock(); //记录时间,使得光标呈现闪烁状态
bool draw_cross = true; //辅助变量,使得光标呈现闪烁状态
// deal with img input
if (img.empty())
{
cout << "Fail to read img!\n"; return 0;
}
int img_x = img.cols - 1, img_y = img.rows - 1;
upper_left.x = 0; upper_left.y = 0;
lower_right.x = img_x; lower_right.y = img_y;
upper_right.x = lower_right.x; upper_right.y = 0;
lower_left.x = 0; lower_left.y = lower_right.y;
cur_point = &upper_left; flag_cur_point = 0;
char key = -1;
int cur_x, cur_y;
cout << "Press Esc to confirm:\n" << endl;
Point size_chose_pre;
size_chose_pre = cvPoint(img_x, img_y);
while (key != 27)
{
img_draw = img.clone();
//画出矩形区域
line(img_draw, upper_left, upper_right, Scalar(0, 255, 0));
line(img_draw, upper_right, lower_right, Scalar(0, 255, 0));
line(img_draw, lower_left, lower_right, Scalar(0, 255, 0));
line(img_draw, upper_left, lower_left, Scalar(0, 255, 0));
loc_roi.x = upper_left.x; loc_roi.y = upper_left.y;
size_roi.x = upper_right.x - upper_left.x;
size_roi.y = lower_left.y - upper_left.y;
if (size_chose_pre != size_roi)
{
cout << "img:" << size_roi.x << "*" << size_roi.y << endl;
size_chose_pre = size_roi;
}
//画出光标,并且呈现闪烁效果
if ((double)(clock() - t_cross) / CLOCKS_PER_SEC >= 0.5)
{
t_cross = clock();
draw_cross = !draw_cross;
}
if (draw_cross == true)
{
cross_u.x = cur_point->x; cross_d.x = cur_point->x;
cross_u.y = cur_point->y - 10 > 0 ? cur_point->y - 10 : 0;
cross_d.y = cur_point->y + 10 < img_y ? cur_point->y + 10 : img_y;
cross_l.y = cur_point->y; cross_r.y = cur_point->y;
cross_l.x = cur_point->x - 10 > 0 ? cur_point->x - 10 : 0;
cross_r.x = cur_point->x + 10 < img_x ? cur_point->x + 10 : img_x;
line(img_draw, cross_u, cross_d, Scalar(0, 0, 255));
line(img_draw, cross_l, cross_r, Scalar(0, 0, 255));
}
imshow("Source img", img_draw);
//响应键盘按键
key = waitKey(10);
switch (key)
{
case 'w':// up
cur_y = cur_point->y - 1; if (cur_y < 0) cur_y = 0; cur_point->y = cur_y;
if (flag_cur_point == 0) { upper_right.y = cur_y; }
else if (flag_cur_point == 1) { upper_left.y = cur_y; }
else if (flag_cur_point == 2) { lower_right.y = cur_y; }
else if (flag_cur_point == 3) { lower_left.y = cur_y; }
else;
break;
case 's':// down
cur_y = cur_point->y + 1; if (cur_y > img_y) cur_y = img_y; cur_point->y = cur_y;
if (flag_cur_point == 0) { upper_right.y = cur_y; }
else if (flag_cur_point == 1) { upper_left.y = cur_y; }
else if (flag_cur_point == 2) { lower_right.y = cur_y; }
else if (flag_cur_point == 3) { lower_left.y = cur_y; }
else;
break;
case 'a':// left
cur_x = cur_point->x - 1; if (cur_x < 0) cur_x = 0; cur_point->x = cur_x;
if (flag_cur_point == 0) { lower_left.x = cur_x; }
else if (flag_cur_point == 1) { lower_right.x = cur_x; }
else if (flag_cur_point == 2) { upper_left.x = cur_x; }
else if (flag_cur_point == 3) { upper_right.x = cur_x; }
else;
break;
case 'd':// right
cur_x = cur_point->x + 1; if (cur_x > img_x) cur_x = img_x; cur_point->x = cur_x;
if (flag_cur_point == 0) { lower_left.x = cur_x; }
else if (flag_cur_point == 1) { lower_right.x = cur_x; }
else if (flag_cur_point == 2) { upper_left.x = cur_x; }
else if (flag_cur_point == 3) { upper_right.x = cur_x; }
else;
break;
case 13:// confirm choice
key = 27;
break;
case 'c':// change file
goto get_filename;
break;
default:
break;
}
}// end for while
destroyAllWindows();
Rect roi(loc_roi.x, loc_roi.y, size_roi.x, size_roi.y);
img_dst = img(roi);
cout << "Please input the filename to save image:\n";
cin >> filename;
//得到保存结果的文件名,检查是否合法
input_type = unknown_type;
dot_loc;
dot_loc = filename.rfind('.');
if (dot_loc == -1) // input filename contains no dot
{
cout << "Wrong input!\n";
return 0;
}
filename_type = filename.substr(dot_loc, filename.length() - 1);
for (int i = 0, i_len = src_type_img->length(); i < i_len; i++)
{
if (filename_type == src_type_img[i])
{
input_type = img_type;
break;
}
}
if (input_type == unknown_type)
{
cout << "Wrong input!\n";
return 0;
}
imwrite(filename, img_dst);
cout << "Image saved as " << filename << "." << endl;
return 0;
}// end for main
void on_Bar(int locFrame, void*)
{
video.set(CV_CAP_PROP_POS_FRAMES, locFrame);
video >> img; //改变img的内容,用于处理
curFrame = locFrame;
}
void on_Mouse(int event, int x, int y, int flags, void* param)
{
double len[4], min_len = 10000000.0;
if (event == CV_EVENT_LBUTTONDOWN)
{
t_rect_delay = clock();
flag_select = true;
p_ul.x = x; p_ul.y = y;
//获得鼠标点击点与当前四个顶点的距离,最近的点更改到鼠标位置
len[0] = sqrt((upper_left.x - x)*(upper_left.x - x) + (upper_left.y - y)*(upper_left.y - y));
len[1] = sqrt((upper_right.x - x)*(upper_right.x - x) + (upper_right.y - y)*(upper_right.y - y));
len[2] = sqrt((lower_left.x - x)*(lower_left.x - x) + (lower_left.y - y)*(lower_left.y - y));
len[3] = sqrt((lower_right.x - x)*(lower_right.x - x) + (lower_right.y - y)*(lower_right.y - y));
for (int i = 0; i < 4; i++)
{
if (min_len > len[i])
min_len = len[i];
}
for (int i = 0; i < 4; i++)
{
if (len[i] == min_len)
{
switch (i)
{
case 0:
cur_point = &upper_left; flag_cur_point = 0;
break;
case 1:
cur_point = &upper_right; flag_cur_point = 1;
break;
case 2:
cur_point = &lower_left; flag_cur_point = 2;
break;
case 3:
cur_point = &lower_right; flag_cur_point = 3;
break;
default:
break;
}// end of switch
break;
}// end of if
}// end of for
}//end of if
else if (event == CV_EVENT_LBUTTONUP)
{
flag_select = false;
}
if (flag_select == true && (clock() - t_rect_delay) * 1000 / CLOCKS_PER_SEC > min_rect_delay)
{
upper_left = p_ul;
if (p_ul.x > x)
{
xmax = p_ul.x; xmin = x;
flag_cur_point = 0;
}
else {
xmax = x; xmin = p_ul.x;
flag_cur_point = 1;
}
if (p_ul.y > y)
{
ymax = p_ul.y; ymin = y;
}
else
{
ymax = y; ymin = p_ul.y;
flag_cur_point += 2;
}
upper_left.x = xmin; upper_left.y = ymin;
lower_right.x = xmax; lower_right.y = ymax;
upper_right.x = lower_right.x; upper_right.y = upper_left.y;
lower_left.x = upper_left.x; lower_left.y = lower_right.y;
switch (flag_cur_point) {
case 0:
cur_point = &upper_left;
break;
case 1:
cur_point = &upper_right;
break;
case 2:
cur_point = &lower_left;
break;
case 3:
cur_point = &lower_right;
break;
default:
break;
}
}//end of if
}// end of on_Mouse