#include #include "alg.h" #include "AlgCommon.h" /* 截图方式说明: 1.鼠标左键按下则为截图起始点 方法一:鼠标左键一直按下,直到鼠标移动到目标点再松开,即可完成一次截图; 方法二:鼠标左键单击后,鼠标移动到目标点再次鼠标左键单击,即可完成一次截图; 若是在截图过程中,单击鼠标右键可取消本次截图状态 2.截图不会立即进行保存,截完图后可以单击鼠标右键进行取消保存上一次的截图; 存储方式一:若是截图完成后,再次进入截图状态,则会自动存储上一轮截图; 存储方式二:若是跳到下一张原始图片,则上一张原始图片中未保存的上一轮截图会自动存储; */ #define MIN_ROI_AREA 300 //有效截图的最小面积限制 //定义全局变量 static cv::Point g_pt; //存储鼠标第一次按下的坐标,即截图的左上角坐标; static cv::Mat g_img_src; //存储当前的原始图像; static cv::Mat g_img_dst; //存储截图过程实时的图像; static cv::Mat g_img_roi; //存储截图完成时的局部图; static cv::Rect roi_area; //存储截图完成时的局部图; static bool g_flag = false; //是否已处于截图状态的标示 static bool g_issave = false; //是否已经有未存储的缓存roi的标示 static std::string g_window_name = "image";//窗口名 //保存截取的roi图像 void save_roi(cv::Mat image) { //定义输出路径,imwrite()输出,略; } // 鼠标的回调函数 static void onMouse(int event, int x, int y, int, void*) { //触发鼠标左键按下事件 if (cv::EVENT_LBUTTONDOWN == event) { //若当前不是截图状态,则进入截图状态,当前点为起始点并存储; if (g_flag == false) { //进入截图状态; g_flag = true; //存储截图起始点 g_pt = cv::Point(x, y); //若未保存上一轮roi的保存,则进行存储; if (g_issave == true) { save_roi(g_img_roi); g_issave = false; } } //若当前处于截图状态,则结束截图状态,并获取roi if (g_flag == true && abs(x - g_pt.x)*abs(y - g_pt.y) > MIN_ROI_AREA) { //结束截图状态 g_flag = false; // 利用截图区域生成g_img_roi roi_area = cv::Rect(g_pt, cv::Point(x, y)); g_img_src(roi_area).copyTo(g_img_roi); //cv::imshow("roi", g_img_roi); g_issave = true; } }//if(CV_EVENT_LBUTTONDOWN == event) //触发鼠标移动事件,并处于截图状态时 else if (cv::EVENT_MOUSEMOVE == event && g_flag) { //重新初始化g_img_dst,确保g_img_dst上始终只有一个框 g_img_src.copyTo(g_img_dst); cv::rectangle(g_img_dst, g_pt, cv::Point(x, y), cv::Scalar(255, 0, 0), 3, 8); cv::imshow(g_window_name, g_img_dst); } //触发鼠标左键松开事件 else if (cv::EVENT_LBUTTONUP == event && abs(x - g_pt.x)*abs(y - g_pt.y) > MIN_ROI_AREA) { //结束截图状态 g_flag = false; //获取roi roi_area = cv::Rect(g_pt, cv::Point(x, y)); g_img_src(roi_area).copyTo(g_img_roi); //cv::imshow("roi", g_img_roi); g_issave = true; } //触发鼠标右键按下事件 else if (cv::EVENT_RBUTTONDOWN == event) { //在截图状态,则取消截图状态 if (g_flag == true) { g_flag = false; //更新图像 cv::imshow(g_window_name, g_img_src); } //不在截图状态,则无效上一轮的截图缓存; if (g_flag == false) { g_issave = false; //更新图像 cv::imshow(g_window_name, g_img_src); //关闭局部窗口 //cv::destroyWindow("roi"); } } } bool inline getMinMax(std::vector &points, cv::Point3i &min, cv::Point3i &max) { int minx = 0, miny = 0, minz = 0; int maxx = 0, maxy = 0, maxz = 0; for (int i = 0; i < points.size(); i++) { if (points[i].x < minx)minx = points[i].x; if (points[i].y < miny)miny = points[i].y; if (points[i].z < minz)minz = points[i].z; if (points[i].x > maxx)maxx = points[i].x; if (points[i].y > maxy)maxy = points[i].y; if (points[i].z > maxz)maxz = points[i].z; } min.x = minx; min.y = miny; min.z = minz; max.x = maxx; max.y = maxy; max.z = maxz; return true; } bool Alg::ScreenShot(cv::Mat imgfull) { cv::namedWindow(g_window_name, cv::WINDOW_AUTOSIZE); cv::setMouseCallback(g_window_name, onMouse, 0); cv::Mat img = imgfull; if (img.empty()) { std::cout << "empty image" << std::endl; return false; } //存储原始图像 g_img_src = img.clone(); cv::imshow(g_window_name, g_img_src); while (1) { cv::waitKey(100); if(g_issave)break; } //图像选择窗口 cv::destroyWindow(g_window_name); img = g_img_roi.clone(); if (img.empty()) { std::cout << "empty roi image" << std::endl; return false; } RefRect = roi_area; return true; } //*****************图像移位函数******************* void dftshift(cv::Mat &img_r, cv::Mat &img_i) { int cx = img_r.cols / 2; int cy = img_r.rows / 2;//以下的操作是移动图像 (零频移到中心) cv::Mat part1_r(img_r, cv::Rect(0, 0, cx, cy)); //元素坐标表示为(cx,cy) cv::Mat part2_r(img_r, cv::Rect(cx, 0, cx, cy)); cv::Mat part3_r(img_r, cv::Rect(0, cy, cx, cy)); cv::Mat part4_r(img_r, cv::Rect(cx, cy, cx, cy)); cv::Mat temp; part1_r.copyTo(temp); //左上与右下交换位置(实部) part4_r.copyTo(part1_r); temp.copyTo(part4_r); part2_r.copyTo(temp); //右上与左下交换位置(实部) part3_r.copyTo(part2_r); temp.copyTo(part3_r); cv::Mat part1_i(img_i, cv::Rect(0, 0, cx, cy)); //元素坐标(cx,cy) cv::Mat part2_i(img_i, cv::Rect(cx, 0, cx, cy)); cv::Mat part3_i(img_i, cv::Rect(0, cy, cx, cy)); cv::Mat part4_i(img_i, cv::Rect(cx, cy, cx, cy)); part1_i.copyTo(temp); //左上与右下交换位置(虚部) part4_i.copyTo(part1_i); temp.copyTo(part4_i); part2_i.copyTo(temp); //右上与左下交换位置(虚部) part3_i.copyTo(part2_i); temp.copyTo(part3_i); } //*****************频率域滤波******************* cv::Mat freqfilt(cv::Mat &scr, cv::Mat &blur) { cv::Mat img_r = scr.clone(); cv::Mat img_i = cv::Mat::zeros(scr.size(), CV_32FC1); //***********************DFT******************* cv::Mat plane[] = { img_r, img_i}; //创建通道,存储dft后的实部与虚部(CV_32F,必须为单通道数) cv::Mat complexIm; merge(plane, 2, complexIm);//合并通道 (把两个矩阵合并为一个2通道的Mat类容器) dft(complexIm, complexIm);//进行傅立叶变换,结果保存在自身 //***************中心化******************** split(complexIm, plane);//分离通道(数组分离) //plane[0] = plane[0](cv::Rect(0, 0, plane[0].cols & -2, plane[0].rows & -2));//这里为什么&上-2具体查看opencv文档 // //其实是为了把行和列变成偶数 -2的二进制是11111111.......10 最后一位是0 dftshift(plane[0], plane[1]); //*****************滤波器函数与DFT结果的乘积**************** cv::Mat BLUR; { cv::Mat blur_r, blur_i; multiply(plane[0], blur, blur_r); //滤波(实部与滤波器模板对应元素相乘) multiply(plane[1], blur, blur_i);//滤波(虚部与滤波器模板对应元素相乘) cv::Mat plane1[] = { blur_r, blur_i }; merge(plane1, 2, BLUR);//实部与虚部合并 } //*********************得到原图频谱图*********************************** //magnitude(plane[0], plane[1], plane[0]);//获取幅度图像,0通道为实部通道,1为虚部,因为二维傅立叶变换结果是复数 //plane[0] += cv::Scalar::all(1); //傅立叶变换后的图片不好分析,进行对数处理,结果比较好看 //log(plane[0], plane[0]); // float型的灰度空间为[0,1]) //normalize(plane[0], plane[0], 1, 0, cv::NORM_MINMAX); //归一化便于显示 //cv::Mat iiimg=plane[0]; //imshow("原图像频谱图",plane[0]); idft(BLUR, BLUR); //idft结果也为复数 split(BLUR, plane);//分离通道,主要获取通道 magnitude(plane[0], plane[1], plane[0]); //求幅值(模) normalize(plane[0], plane[0], 1, 0, cv::NORM_MINMAX); //归一化便于显示 return plane[0];//返回参数 } cv::Mat ideal_lbrf_kernel(cv::Mat &scr, float sigma) { cv::Mat ideal_low_pass(scr.size(), CV_32FC1); //,CV_32FC1 cv::Mat gauss_low_pass(scr.size(), CV_32FC1); //,CV_32FC1 cv::Mat gauss_high_pass(scr.size(), CV_32FC1); //,CV_32FC1 ////*****************理想低通滤波器*********************** //float d0 = sigma;//半径D0越小,模糊越大;半径D0越大,模糊越小 //for (int i = 0; i < scr.rows; i++) { // for (int j = 0; j < scr.cols; j++) { // double d = sqrt(pow((i - scr.rows / 2), 2) + pow((j - scr.cols / 2), 2));//分子,距离频率中心的距离,计算pow必须为float型 // if (d <= d0) { // ideal_low_pass.at(i, j) = 1; // } // else { // ideal_low_pass.at(i, j) = 0; // } // } //} //std::string name = "理想低通滤波器d0=" + std::to_string(sigma); //*****************高斯带通低通滤波器*********************** //float sigma_gauss_low = sigma; //for (int i = 0; i < scr.rows; i++) { // for (int j = 0; j < scr.cols; j++) { // double d = sqrt(pow((i - scr.rows / 2), 2) + pow((j - scr.cols / 2), 2));//分子,计算pow必须为float型 // double gauss_value = -1 * pow(d, 2) / (2 * pow(sigma_gauss_low, 2)); // gauss_value = pow(2.718281828459, gauss_value); // gauss_low_pass.at(i, j) = gauss_value; // } //} //name = "高斯低通滤波器d0=" + std::to_string(sigma); //*****************高斯带通高通滤波器*********************** float sigma_gauss_high = sigma; for (int i = 0; i < scr.rows; i++) { for (int j = 0; j < scr.cols; j++) { double d = sqrt(pow((i - scr.rows / 2), 2) + pow((j - scr.cols / 2), 2));//分子,计算pow必须为float型 double gauss_value = -1 * pow(d, 2) / (2 * pow(sigma_gauss_high, 2)); gauss_value = pow(2.718281828459, gauss_value); gauss_high_pass.at(i, j) = 1-gauss_value; } } //name = "高斯低通滤波器d0=" + std::to_string(sigma); return gauss_high_pass; } cv::Mat ideal_Low_Pass_Filter(cv::Mat &src, float sigma) { int M = cv::getOptimalDFTSize(src.rows); int N = cv::getOptimalDFTSize(src.cols); cv::Mat padded; //调整图像加速傅里叶变换 copyMakeBorder(src, padded, 0, M - src.rows, 0, N - src.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0)); padded.convertTo(padded, CV_32FC1); //将图像转换为flaot型 cv::Mat ideal_kernel = ideal_lbrf_kernel(padded, sigma);//理想低通滤波器 cv::Mat result = freqfilt(padded, ideal_kernel); return result; } void Alg::process_2dfft(cv::Mat &input,cv::Mat &output) { double t = (double)cv::getTickCount(); cv::Mat img_clone = input.clone(); resize(img_clone, input,cv::Size(512,512)); cv::Mat img_thr; threshold(img_clone, img_thr, 200, 255, cv::THRESH_TOZERO_INV); threshold(img_thr, img_thr, 50, 255, cv::THRESH_BINARY); cv::Mat pro_src = img_thr.clone(); fistEROthenDIL(pro_src,3, 3); //查找轮廓 double maxarea = 0; int maxAreaIdx = 0; std::vector > contours_pre; std::vector < cv::Vec4i > hierarchy_pre; cv::findContours(pro_src, contours_pre, hierarchy_pre, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); for (int i = 0; i < contours_pre.size(); i++) { double tmparea = fabs(contourArea(contours_pre[i])); if (tmparea > maxarea) { maxarea = tmparea; maxAreaIdx = i;//记录最大轮廓的索引号 } } cv::Mat second_input; if (contours_pre.size()) { cv::Rect rect_pre = cv::boundingRect(contours_pre[maxAreaIdx]); second_input = img_clone(rect_pre).clone(); } else { second_input = img_clone; } cv::Mat ideal = ideal_Low_Pass_Filter(second_input, 30); ideal = ideal(cv::Rect(0, 0, second_input.cols, second_input.rows)); cv::Mat third_input; normalize(ideal, third_input, 0, 255, cv::NORM_MINMAX); third_input.convertTo(third_input, CV_8UC1); threshold(third_input, third_input, 10, 255, cv::THRESH_BINARY); fistEROthenDIL(third_input, 3, 5); output = third_input.clone(); cv::waitKey(1); t = ((double)cv::getTickCount() - t) / cv::getTickFrequency(); std::cout << "cost time\t" << t <<" s" << std::endl; } bool Alg::process_bool(cv::Mat &input, cv::Mat &output) { double t = (double)cv::getTickCount(); cv::Mat img_clone = input.clone(); cv::Mat img_input; threshold(img_clone, img_clone, 200, 0, cv::THRESH_TOZERO_INV); threshold(img_clone, img_input, 90, 0, cv::THRESH_TOZERO); // third_input.convertTo(third_input, CV_8UC1); fistEROthenDIL(img_input, 13, 13); // output = third_input.clone(); // //查找轮廓 // cv::Mat temp = third_input.clone(); // double maxarea = 0; // int maxAreaIdx = 0; // std::vector > contours_pre; // std::vector < cv::Vec4i > hierarchy_pre; // cv::findContours(temp, contours_pre, hierarchy_pre, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); // for (int i = 0; i < contours_pre.size(); i++) // { // drawContours(temp, contours_pre, i, cv::Scalar(255), 2, 8, hierarchy_pre); // double tmparea = fabs(contourArea(contours_pre[i])); // if (tmparea > maxarea) // { // maxarea = tmparea; // maxAreaIdx = i;//记录最大轮廓的索引号 // } // } t = ((double)cv::getTickCount() - t) / cv::getTickFrequency(); std::cout << "cost time\t" << t << " s" << std::endl; return 1; }