离散傅里叶变换
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:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Erosion Demo", &erosionElem, maxElem, Erosion); 62 cv::createTrackbar("Kernel size:\n 2n + 1", "Erosion Demo", &erosionSize, maxKernelSize, Erosion); 63 64 cv::createTrackbar("Element:\n 0: Rect \n 1: Cross \n 2: Ellipse", "Dilation Demo", &dilationElem, maxElem, Dilation); 65 cv::createTrackbar("Kernel size:\n 2n + 1", "Dilation Demo", &dilationSize, maxKernelSize, Dilation); 66 67 Erosion(0, 0); 68 Dilation(0, 0); 69 cv::waitKey(0); 70 return 0; 71 }