|
|
#include <iostream>
|
|
|
#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<cv::Point3i> &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<float>(i, j) = 1;
|
|
|
// }
|
|
|
// else {
|
|
|
// ideal_low_pass.at<float>(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<float>(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<float>(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<std::vector<cv::Point> > 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<std::vector<cv::Point> > 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;
|
|
|
} |