离散傅里叶变换
1 // 离散傅里叶变换
2 /*
3 离散傅里叶变换(DFT),是傅里叶变换在时域和频域上都呈现离散的形式,将时域信号
4 的采样变换为在离散时间傅里叶变换(DTFT)频域的采样。在形式上,变换两端(时域和
5 频域上)的序列是有限长的,而实际上这两组序列都应当被认为是离散周期信号的主值序
6 列。即使对有限长的离散信号作DFT,也应当将其看作经过周期延拓成为周期信号再作变
7 换。在实际应用中通常采用快速傅里叶变换以高效计算DFT。
8 */
9 int dftExample(void) {
10 cv::Mat image = cv::imread("sky.jpg", cv::IMREAD_COLOR);
11 //assert(!image.empty(), "Failed read!");
12 cv::Mat padded;
13 // getOptimalDFTSize函数返回给定向量尺寸的傅里叶最优尺寸大小。
14 // 此函数的唯一一个参数为int类型的vecsize,向量尺寸,即图片的rows、cols
15 int m = cv::getOptimalDFTSize(image.rows);
16 int n = cv::getOptimalDFTSize(image.cols);
17 // 扩展输入图像image尺寸,在边缘加0值
18 // 滤波 https://blog.csdn.net/qianqing13579/article/details/42323397
19 cv::copyMakeBorder(image, padded, 0, m - image.rows, 0, n - image.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
20
21 cv::Mat planes[] = { cv::Mat_<float>(padded), cv::Mat::zeros(padded.size(), CV_32F) };
22 cv::Mat complexImage;
23 cv::merge(planes, 2, complexImage); // 图像通道的合并
24
25 cv::dft(complexImage, complexImage); // 对一维或者二维浮点数数组进行正向或反向离散傅里叶变换
26 // 运行到此处 抛出异常 程序终止了
27
28
29
30 cv::split(complexImage, planes);
31 // 计算二维矢量的幅值:magnitude()函数
32 // https://blog.csdn.net/qq_31935691/article/details/71699582
33 cv::magnitude(planes[0], planes[1], planes[0]);
34 cv::Mat magI = planes[0];
35 magI += cv::Scalar::all(1);
36 cv::log(magI, magI);
37
38 magI = magI(cv::Rect(0, 0, magI.cols & -2, magI.rows & -2));
39 int cx = magI.cols / 2;
40 int cy = magI.rows / 2;
41
42 // 将magI从横竖中间线切开 分为四部分
43 cv::Mat q0(magI, cv::Rect(0, 0, cx, cy)); // 左上
44 cv::Mat q1(magI, cv::Rect(cx, 0, cx, cy)); // 右上
45 cv::Mat q2(magI, cv::Rect(0, cy, cx, cy)); // 左下
46 cv::Mat q3(magI, cv::Rect(cx, cy, cx, cy)); // 右下
47
48 cv::Mat tmp;
49 // 交换q0 q3
50 q0.copyTo(tmp);
51 q3.copyTo(q0);
52 tmp.copyTo(q3);
53 // 交换q1 q2
54 q1.copyTo(tmp);
55 q2.copyTo(q1);
56 tmp.copyTo(q2);
57
58 // 归一化数据。该函数分为范围归一化与数据值归一化。
59 // https://blog.csdn.net/cosmispower/article/details/64457406
60 cv::normalize(magI, magI, 0, 1, cv::NORM_MINMAX);
61
62 cv::imshow("Input Image", image);
63 cv::imshow("Spectrum Magnitude", magI);
64
65 cv::waitKey(0);
66 return 0;
67 }
// OpenCV中的英特尔IPP异步C/C++库
void ippasyncExample() {
}
// 使用OpenCV parallel_for 来并行化代码
void parallelForExample() {
}
滤波操作
1 class SmoothingDemo {
2 // OpenCV平滑图像
3 // 平滑 也称为模糊 是一种常用的图像处理操作 使用平滑的原因有很多 此处是为了减少噪音
4 // 要执行平滑操作 需要对图像应用滤波器
5 // 有多种类型的滤波器 最常见的是线性滤波器 除此之外还有
6 // 归一化框滤波器
7 // 高斯滤波器
8 // 中值滤波器
9 // 双边滤波器
10 public:
11 SmoothingDemo() {
12 windowName = "Smoothing Demo";
13 cv::namedWindow(windowName, cv::WINDOW_AUTOSIZE);
14 }
15
16 int showDifferentSmoothing() {
17 src = cv::imread("angel.jpg", cv::IMREAD_COLOR);
18 // show original image
19 if (displayCaption("Original Image") != 0)
20 return 0;
21
22 dst = src.clone();
23 if (displayDst(DELAY_CAPTION) != 0) // 展示clone后的目标图片
24 return 0;
25
26 // // 归一化滤波
27 if (displayCaption("Homogeneous Blur") != 0)
28 return 0;
29 for (int i = 1; i < MAX_KERNEL_LENGTH; i += 2) {
30 cv::blur(src, dst, cv::Size(i, i), cv::Point(-1, -1)); // https://blog.csdn.net/duwangthefirst/article/details/79971322
31 if (displayDst(DELAY_BLUR) != 0)
32 return 0;
33 }
34
35 // 高斯滤波
36 if (displayCaption("Gaussian Blur") != 0)
37 return 0;
38 for (int i = 1; i < MAX_KERNEL_LENGTH; i += 2) {
39 cv::GaussianBlur(src, dst, cv::Size(i, i), 0, 0); // https://blog.csdn.net/cindywry/article/details/102837184
40 if (displayDst(DELAY_BLUR) != 0)
41 return 0;
42 }
43
44 // 中值滤波
45 if (displayCaption("Median Blur") != 0)
46 return 0;
47 for (int i = 1; i < MAX_KERNEL_LENGTH; i += 2) {
48 cv::medianBlur(src, dst, i); // https://blog.csdn.net/charce_you/article/details/100177628
49 if (displayDst(DELAY_BLUR) != 0)
50 return 0;
51 }
52
53 // 双边滤波
54 if (displayCaption("Bilateral Blur") != 0)
55 return 0;
56 for (int i = 1; i < MAX_KERNEL_LENGTH; i += 2) {
57 cv::bilateralFilter(src, dst, i, i * 2, i / 2); // https://blog.csdn.net/keith_bb/article/details/54427779
58 if (displayDst(DELAY_BLUR) != 0)
59 return 0;
60 }
61
62 // 随着上述各个for循环的进行 i 的增大 图片越来越模糊
63
64 displayCaption("End: Press a key!");
65 cv::waitKey(0);
66 return 0;
67 }
68
69 private:
70 int displayCaption(const char* caption) {
71 dst = cv::Mat::zeros(src.size(), src.type());
72 cv::putText(dst, caption, cv::Point(src.cols / 4, src.rows / 2), cv::FONT_HERSHEY_COMPLEX, 1, cv::Scalar(255, 255, 255));
73 cv::imshow(windowName, dst);
74 int c = cv::waitKey(DELAY_CAPTION);
75
76 if (c >= 0)
77 return -1;
78 return 0;
79 }
80
81 int displayDst(int delay) {
82 cv::imshow(windowName, dst);
83 int c = cv::waitKey(delay);
84
85 if (c >= 0)
86 return -1;
87 return 0;
88 }
89
90 static const int DELAY_CAPTION = 1500;
91 static const int DELAY_BLUR = 100;
92 static const int MAX_KERNEL_LENGTH = 31;
93 string windowName;
94 cv::Mat src;
95 cv::Mat dst;
96 };
侵蚀、扩张
1 // 侵蚀 使图像中暗去增长
2 // 扩张 使图像中亮区增长
3 cv::Mat src;
4 cv::Mat erosionDst;
5 cv::Mat dilationDst;
6 int erosionElem = 0;
7 int erosionSize = 0;
8 int dilationElem = 0;
9 int dilationSize = 0;
10 const int maxElem = 2;
11 const int maxKernelSize = 21;
12
13 static void Erosion(int, void*) {
14 int erosionType = 0;
15 if (erosionElem == 0)
16 erosionType = cv::MORPH_RECT;
17 else if (erosionElem == 1)
18 erosionType = cv::MORPH_CROSS;
19 else if (erosionElem == 2)
20 erosionType = cv::MORPH_ELLIPSE;
21 else {}
22
23 // cv::getStructuringElement()返回指定形状和尺寸的结构元素
24 // https://blog.csdn.net/kksc1099054857/article/details/76569718
25 cv::Mat element = cv::getStructuringElement(erosionType, cv::Size(2 * erosionSize + 1, 2 * erosionSize + 1), cv::Point(erosionSize, erosionSize));
26 // erode()函数可以对输入图像用特定结构元素进行腐蚀操作,该结构元素确定腐蚀操作过程中的邻域的形状,各点像素值将被替换为对应邻域上的最小值
27 // https://blog.csdn.net/duwangthefirst/article/details/79999856
28 cv::erode(src, erosionDst, element);
29 cv::imshow("Erosion Demo", erosionDst);
30 }
31
32 static void Dilation(int, void*) {
33 int dilationType = 0;
34 if (dilationElem == 0)
35 dilationType = cv::MORPH_RECT;
36 else if (dilationElem == 1)
37 dilationType = cv::MORPH_CROSS;
38 else if (dilationElem == 2)
39 dilationType = cv::MORPH_ELLIPSE;
40 else {}
41
42 cv::Mat element = cv::getStructuringElement(dilationType, cv::Size(2 * dilationSize + 1, 2 * dilationSize + 1), cv::Point(dilationSize, dilationSize));
43 // dilate()函数可以对输入图像用特定结构元素进行膨胀操作,该结构元素确定膨胀操作过程中的邻域的形状,各点像素值将被替换为对应邻域上的最大值:
44 // https://blog.csdn.net/duwangthefirst/article/details/80001106
45 cv::dilate(src, dilationDst, element);
46 cv::imshow("Dilation Demo", dilationDst);
47 }
48
49 int ErosionAndDilationExample(void) {
50 src = cv::imread("blackWord.png", cv::IMREAD_COLOR);
51 if (src.empty()) {
52 std::cout << "Failed Read!" << std::endl;
53 return -1;
54 }
55
56 cv::namedWindow("Erosion Demo", cv::WINDOW_AUTOSIZE);
57 cv::namedWindow("Dilation Demo", cv::WINDOW_AUTOSIZE);
58
59 // 此处最后一个参数不匹配?
60 // 但是这个是void Fun(int, void*);类型的
61 cv::createTrackbar("Element:
0: Rect
1: Cross
2: Ellipse", "Erosion Demo", &erosionElem, maxElem, Erosion);
62 cv::createTrackbar("Kernel size:
2n + 1", "Erosion Demo", &erosionSize, maxKernelSize, Erosion);
63
64 cv::createTrackbar("Element:
0: Rect
1: Cross
2: Ellipse", "Dilation Demo", &dilationElem, maxElem, Dilation);
65 cv::createTrackbar("Kernel size:
2n + 1", "Dilation Demo", &dilationSize, maxKernelSize, Dilation);
66
67 Erosion(0, 0);
68 Dilation(0, 0);
69 cv::waitKey(0);
70 return 0;
71 }