OPENCV C++(九)鼠标响应+dft+idft

鼠标响应回调函数(固定格式)

cpp 复制代码
void on_mouse(int EVENT, int x, int y, int flags, void* userdata)
{

	Mat hh;
	hh = *(Mat*)userdata;
	Point p(x, y);
	switch (EVENT)
	{
	case EVENT_LBUTTONDOWN:
	{
		points.x = x;
		points.y = y;
		mousePoints.push_back(points);
		circle(hh, points, 4, cvScalar(255, 255, 255), -1);
		imshow("mouseCallback", hh);
	}
	break;
	}

}

这个鼠标响应函数 就是鼠标左键按下后,会标记一个点,画一个白色点,并将这个点存储到points当中

鼠标响应函数

cpp 复制代码
setMouseCallback("mouseCallback", on_mouse, &selectMat);

mousecallback就是窗口名称,后面是图像,中间是函数名

选取多边形roi区域函数(结合鼠标响应)

cpp 复制代码
int selectPolygon(cv::Mat srcMat, cv::Mat& dstMat)
{

	vector<vector<Point>> contours;
	cv::Mat selectMat;

	cv::Mat m = cv::Mat::zeros(srcMat.size(), CV_32F);

	m = 1;

	if (!srcMat.empty()) {
		srcMat.copyTo(selectMat);
		srcMat.copyTo(dstMat);
	}
	else {
		std::cout << "failed to read image!:" << std::endl;
		return -1;
	}

	namedWindow("mouseCallback");
	imshow("mouseCallback", selectMat);
	setMouseCallback("mouseCallback", on_mouse, &selectMat);
	waitKey(0);
	destroyAllWindows();
	//计算roi
	contours.push_back(mousePoints);
	if (contours[0].size() < 3) {
		std::cout << "failed to read image!:" << std::endl;
		return -1;
	}

	drawContours(m, contours, 0, Scalar(0), -1);

	m.copyTo(dstMat);

	return 0;
}

建立一个全白色的mat,用鼠标响应选取白色点连接构成roi,然后通过drawcontous把roi在全白色mat里面画出,选中区域为黑色,没有选中区域为白色。

也可以将其改成选中的区域为白色 没有选中为黑色 只需要将

m=0;scalar(255,255,255)即可

查看一张图片的幅度频谱(输入灰度)

预处理

cpp 复制代码
Mat padMat;
	//当图像的尺寸是2,3,5的整数倍时,离散傅里叶变换的计算速度最快。	
	//获得输入图像的最佳变换尺寸
	int m = getOptimalDFTSize(srcMat.rows);
	int n = getOptimalDFTSize(srcMat.cols);
cpp 复制代码
copyMakeBorder(srcMat, padMat, 0, m - srcMat.rows, 0, n - srcMat.cols, BORDER_CONSTANT, Scalar::all(0));
//对边界进行填充,刚才得到的m,n

//把灰度图像放在左上角,在右边和下边扩展图像,扩展部分填充为0;

cpp 复制代码
//定义一个数组,存储频域转换成float类型的对象,再存储一个和它一样大小空间的对象来存储复数部分
	Mat planes[] = { Mat_<float>(padMat), Mat::zeros(padMat.size(), CV_32F) };
cpp 复制代码
Mat complexMat;
merge(planes, 2, complexMat);
//将2个单通道的图像合成一幅多通道图像,将实部和虚部组合成一个

正片开始,进行傅里叶变换

cpp 复制代码
dft(complexMat, complexMat);
//输入输出图像
cpp 复制代码
split(complexMat, planes);
//求相位,保存在planes[0]
magnitude(planes[0], planes[1], planes[0]);
//0代表实部,1代表虚部 保存在0中 相位

为了显示方便,而作后续处理

cpp 复制代码
Mat magMat = planes[0];
magMat += Scalar::all(1);
log(magMat, magMat);
//值都很大,取个对数显示出来

把四个角(高频)移到中间来

cpp 复制代码
//确保对称
	magMat = magMat(Rect(0, 0, magMat.cols & -2, magMat.rows & -2));
	int cx = magMat.cols / 2;
	int cy = magMat.rows / 2;
	//将图像移相
	/*
	0 | 1         3 | 2
	-------  ===> -------
	2 | 3         1 | 0
	*/
	Mat q0(magMat, Rect(0, 0, cx, cy));
	Mat q1(magMat, Rect(cx, 0, cx, cy));
	Mat q2(magMat, Rect(0, cy, cx, cy));
	Mat q3(magMat, Rect(cx, cy, cx, cy));
	Mat tmp;
	q0.copyTo(tmp);
	q3.copyTo(q0);
	tmp.copyTo(q3);
	q1.copyTo(tmp);
	q2.copyTo(q1);
	tmp.copyTo(q2);

要一个中间变量temp不然会损失图像,保证对称是怕/2除不整,//修剪频谱,如果图像的行或者列是奇数的话,那其频谱是不对称的,因此要修剪

将图像归一化到0-255 也就是0-1之间可以显示出来

cpp 复制代码
normalize(magMat, magMat, 0, 1, NORM_MINMAX);
	magMat = magMat * 255;
	magMat.copyTo(dstMat);

	return 0;

这样就得到了频谱

求相位谱

cpp 复制代码
phase(planes[0], planes[1], ph);//相位谱ph

用鼠标响应处理频谱,再ifft转换回来

前面的一样直到

cpp 复制代码
mag = mag / 255;

先倒回去

鼠标响应

cpp 复制代码
cv::Mat mask;
Mat proceMag;

selectPolygon(mag,mask);

mag= mag.mul(mask);
proceMag = mag * 255;
imwrite("处理后频谱.jpg", proceMag);

倒过来干 ifft

cpp 复制代码
//前述步骤反着来一遍,目的是为了逆变换回原图
	Mat q00(mag, Rect(0, 0, cx, cy));   	
	Mat q10(mag, Rect(cx, 0, cx, cy));  
	Mat q20(mag, Rect(0, cy, cx, cy));  
	Mat q30(mag, Rect(cx, cy, cx, cy)); 
	
	//交换象限
	q00.copyTo(tmp);
	q30.copyTo(q00);
	tmp.copyTo(q30);
	q10.copyTo(tmp);
	q20.copyTo(q10);
	tmp.copyTo(q20);

	mag = mag * maxVal;//将归一化的矩阵还原 
	exp(mag, mag);//对应于前述去对数
	mag = mag - Scalar::all(1);//对应前述+1
	polarToCart(mag, ph, planes[0], planes[1]);//由幅度谱mag和相位谱ph恢复实部planes[0]和虚部planes[1]
	merge(planes, 2, complexImg);//将实部虚部合并

正片开始 idtf

cpp 复制代码
Mat ifft(Size(src.cols, src.rows), CV_8UC1);
	//傅里叶逆变换
	idft(complexImg, ifft, DFT_REAL_OUTPUT);
	normalize(ifft, ifft, 0, 1, CV_MINMAX);

	Rect rect(0, 0, src.cols, src.rows);
	dst = ifft(rect);
	dst = dst * 255;

	cv::Mat dspMat;
	dst.convertTo(dspMat, CV_8UC1);
	imshow("dst", dspMat);
	imshow("src", src);
	waitKey(0);

	return 0;

注意这里有个转换 idft类型要转换成8UC1才能展示

这里有个疑问 mag不×255才能显示出图像 ×了反而显示出全白

但是imwirte出的图像又是正常的 为什么?

相关推荐
技术与健康21 分钟前
LLM实践系列:利用LLM重构数据科学流程07 - 工程化实践与挑战
人工智能·机器学习·重构·大模型工程化实践
MobotStone31 分钟前
AI Agent工程师≠Prompt工程师:能力断层在哪
人工智能
深瞳智检34 分钟前
目标检测数据集 第007期-基于yolo标注格式的茶叶病害检测数据集(含免费分享)
人工智能·深度学习·yolo·目标检测·计算机视觉
区块链蓝海34 分钟前
UPCX与日本电信公司NTT就新一代去中心化支付系统签署合作协议
人工智能·web3·区块链
berling001 小时前
【论文阅读 | arXiv 2025 | WaveMamba:面向RGB-红外目标检测的小波驱动Mamba融合方法】
论文阅读·人工智能·目标检测
CHEN5_021 小时前
时序数据库选型“下半场”:从性能竞赛到生态博弈,四大主流架构深度横评
数据库·人工智能·ai·架构·时序数据库
top_designer1 小时前
作品集PDF又大又卡?我用InDesign+Acrobat AI构建轻量化交互式文档工作流
人工智能·pdf·自动化·设计规范·acrobat·indesign·交互式pdf
涡能增压发动积1 小时前
MySQL数据库为何逐渐黯淡,PostgreSQL为何能新王登基
人工智能·后端
瓦力wow1 小时前
Pytorch安装详细步骤
人工智能·pytorch·python
Java中文社群1 小时前
重磅!Ollama发布UI界面,告别命令窗口!
java·人工智能·后端