鼠标响应回调函数(固定格式)
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出的图像又是正常的 为什么?