You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Cigarette/CigaretteSingle/alg.cpp

392 lines
12 KiB
C++

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#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型的灰度空间为[01])
//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;
}