最近一直在看论文写报告,在这个过程中想要把论文中的图片某一部分精确的截下来;尤其是在Ubuntu系统下,截图一直靠自己编写小程序实现,所以干脆编一个截图程序一直用。今天在这里分享给大家,给想学习OpenCV或者编写程序练手的同学一个尽量详细的教程。

使用简介:

1. 程序输入:

需要打开的文件名,可以是图片或者视频,输入的是相对路径或者绝对路径。例如程序在桌面上,同时桌面上有一个名为test.mp4的视频。输入的文件名可以是相对路径“test.mp4”或者绝对路径“C:\Users\XXX\Desktop\test.mp4”。程序自动识别输入文件是视频还是图片,无法自动识别时会要求手动输入类型。

2. 程序操作:

按住鼠标拖动调整矩形框,每次点击会激活离点击点最近的一个矩形框顶点,通过按键‘w’‘s’‘a’‘d’四个按键微调这个点的位置;视频输入可以通过滑动条选取想要的一帧图像;点击Esc键确定选取范围或者按‘c’键重新输入新的文件。

3. 使用演示:

由于程序中有参数处理的部分,所以演示时通过命令行启动程序,也可以双击直接启动。

Java opencv截取图像某一区域 opencv屏幕截图_鼠标响应


下面是截取的结果:

Java opencv截取图像某一区域 opencv屏幕截图_视频进度条控制_02

源代码:

注意:此代码仅在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