环境:OpenCV3.2.0,VS2013,Windows7。
语言:C++
我需要截取样本作机器学习数据的训练,不但要能方便截图,而且截取的图片必须是固定的尺寸,这样的软件不好找,于是我便打算自己编写一个。
效果如下:可以看到,图片中所有的矩形框都是1:1的正方形!也就是说,我的截图程序,能把截图时鼠标拖动的矩形自动变为正方形,而且最重要的是,自动变化的矩形框尺寸不会超出图像尺寸。
这个程序有如下3个难点:鼠标事件的变化; 自动变化的尺寸不超出图像尺寸;需要考虑到鼠标从上往下、从下往上、从左向右、从右向左 等多种拖动方式
图片.png
感悟:
1、程序一定要考虑到图像的尺寸,不能超出图像的尺寸,否则程序就会出错。 尺寸可以把 长(cols)、 宽(rows)分开考虑,这两个值是独立的,如果把他们综合在一起思考容易把自己搞糊涂。
代码如下:
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <string>
#include <ctime>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include <direct.h> //windows下C++创建文件夹头文件
//#include <stdlib.h> //srand()和rand()函数
//#include <time.h>
using namespace cv;
using namespace std;
// global variable
static Mat g_img_src;
static Mat g_img_dst;
static Mat g_imgMouseMove;
static Mat g_img_Cut;
static bool g_MouseDown_flag = false;
static Point g_startPoint;
static string g_window_name = "image";
int P = 0; //图像缩放比例
static void onMouse(int event, int x, int y, int, void*)
{
if (CV_EVENT_LBUTTONDOWN == event){ //按下鼠标事件
g_MouseDown_flag = true; //方框标志位置为真
g_startPoint = Point(x, y); //记录下方框的起始位置
}
else if (CV_EVENT_MOUSEMOVE == event && g_MouseDown_flag){ //拖动鼠标事件
// rectangle(Mat& img, Point pt1, Point pt2, const Scalar& color, int thickness=1, int lineType=8, int shift=0)
// img 图像;
// pt1 矩形的一个顶点;
// pt2 矩形对角线上的另一个顶点;
// color 线条颜色(RGB) 或亮度(灰度图像 )(grayscale image)。
// thickness 组成矩形的线条的粗细程度,取负值时(如 CV_FILLED)函数绘制填充了色彩的矩形。
// line_type 线条的类型,见cvLine的描述;
// shift 坐标点的小数点位数。
x = x <= (g_img_src.cols-1) ? x : (g_img_src.cols-1);
y = y <= (g_img_src.rows-1) ? y : (g_img_src.rows-1);
x = x >= 0 ? x : 0;
y = y >= 0 ? y : 0;
g_img_dst.copyTo(g_imgMouseMove);
rectangle(g_imgMouseMove, g_startPoint, Point(x, y), Scalar_<uchar>::all(20), 3, 8); //在g_imgMouseMove上画鼠标拖动时的矩形
//rectangle(g_img_src, g_startPoint, Point(x, y), Scalar_<uchar>::all(20), 5, 8);
// rectangle(g_img_dst, g_startPoint, Point(x, y), Scalar(g_rng.uniform(100, 255), g_rng.uniform(100, 255), g_rng.uniform(100, 255)));
// rectangle(g_img_dst, g_startPoint, Point(x, y), Scalar(0, 255, 0));
imshow(g_window_name, g_imgMouseMove); //把复制的图像显示出来
}
else if (CV_EVENT_LBUTTONUP == event /* && g_startPoint != Point(x, y)*/){ //抬起鼠标事件
x = x <= (g_img_src.cols - 1) ? x : (g_img_src.cols - 1);
y = y <= (g_img_src.rows - 1) ? y : (g_img_src.rows - 1);
x = x >= 0 ? x : 0;
y = y >= 0 ? y : 0;
Point centerPoint = Point((x + g_startPoint.x) / 2, (y + g_startPoint.y)/2);
//取边长大值
int length = abs(x - g_startPoint.x) > abs(y - g_startPoint.y) ? abs(x - g_startPoint.x) : abs(y - g_startPoint.y);
int length_x = (x - g_startPoint.x) / abs(x - g_startPoint.x) * length; //得到各自边长的正负
int length_y = (y - g_startPoint.y) / abs(y - g_startPoint.y) * length;
g_startPoint.x = centerPoint.x - length_x / 2;
g_startPoint.y = centerPoint.y - length_y / 2;
if (g_startPoint.x + length_x > g_img_dst.cols){
g_startPoint.x = g_img_dst.cols - length;// abs(length_x);
}
if (g_startPoint.x + length_x < 0){
g_startPoint.x = length;
}
if (g_startPoint.y + length_y > g_img_dst.rows){
g_startPoint.y = g_img_dst.rows - length;// abs(length_y);
}
if (g_startPoint.y + length_y < 0){
g_startPoint.y = length;
}
x = g_startPoint.x + length_x;
y = g_startPoint.y + length_y;
cout << length_x << " " << length_y << endl;
rectangle(g_img_dst, g_startPoint, Point(x, y), Scalar_<uchar>::all(20), 3, 8); //在img_dst上画矩形框
Point cutPoint;
g_img_src(Rect(g_startPoint*P, Point(x*P, y*P))).copyTo(g_img_Cut); //保存矩形框中的图片:把起始点和结束点的坐标赋给感兴趣区域,并把感兴趣区域赋给g_img_Cut
// imshow("sub image", g_img_Cut);
srand(time(NULL)); //设置随机数种子
imwrite("截取后的图片\\"+to_string(rand() % 100) + to_string(rand() % 300) + to_string(rand() % 20) + to_string(rand() % 30) + ".jpg", g_img_Cut);
//
g_MouseDown_flag = false; //清零鼠标按下标志
}
}
int main(int argc, char** argv){
if (_mkdir("截取后的图片")==0){
cout << "保存文件夹创建成功" << endl;
}
P = 2; //图像尺寸除以4,以减小图片尺寸
g_img_src = imread("1.jpg"); //读取图片
if (!g_img_src.data){
cout << "图片读取失败,请检查图片的名称或路径" << endl;
return 0;
}
g_img_src.copyTo(g_img_dst);
resize(g_img_dst, g_img_dst, Size(g_img_dst.cols / P, g_img_dst.rows / P));
if (g_img_src.empty()){
cerr << "读取文件错误" << endl;
return EXIT_FAILURE; //C语言中执行失败返回的值
}
namedWindow(g_window_name, 0);//设置窗口属性 //CV_WINDOW_NORMAL //CV_WINDOW_KEEPRATIO
// 窗口的名字 鼠标事件发生时的函数指针 默认值为0
setMouseCallback(g_window_name, onMouse, 0); //开启鼠标事件
while (true){
imshow(g_window_name, g_img_dst);
int c = waitKey(0);
if ((c & 255) == 27){ // 按下Esc键
destroyAllWindows(); //关闭所有窗口
cout << "一张图片截图完毕" << endl;
break;
}
}
return EXIT_SUCCESS;
}
网友评论