4.5.1 导向滤波
导向滤波能实现:双边滤波的边缘平滑;在检测到边缘附近
应用:图像增强、HDR压缩、图像抠图以及图像去雾等
1 #include <iostream> 2 #include "opencv2/core/core.hpp" 3 #include "opencv2/highgui/highgui.hpp" 4 #include "opencv2/imgproc/imgproc.hpp" 5 using namespace std; 6 using namespace cv; 7 // 导向滤波器 8 cv::Mat guidedfilter( Mat &srcImage, Mat &srcClone, int r, double eps ) 9 { 10 // 转换源图像信息 11 srcImage.convertTo(srcImage, CV_64FC1); 12 srcClone.convertTo(srcClone, CV_64FC1); 13 int nRows = srcImage.rows; 14 int nCols = srcImage.cols; 15 cv::Mat boxResult; 16 // 步骤一: 计算均值 17 cv::boxFilter(cv::Mat::ones(nRows, nCols, srcImage.type()), 18 boxResult, CV_64FC1, cv::Size(r, r)); 19 // 生成导向均值mean_I 20 cv::Mat mean_I; 21 cv::boxFilter(srcImage, mean_I, CV_64FC1, cv::Size(r, r)); 22 // 生成原始均值mean_p 23 cv::Mat mean_p; 24 cv::boxFilter(srcClone, mean_p, CV_64FC1, cv::Size(r, r)); 25 // 生成互相关均值mean_Ip 26 cv::Mat mean_Ip; 27 cv::boxFilter(srcImage.mul(srcClone), mean_Ip, 28 CV_64FC1, cv::Size(r, r)); 29 cv::Mat cov_Ip = mean_Ip - mean_I.mul(mean_p); 30 // 生成自相关均值mean_II 31 cv::Mat mean_II; 32 cv::boxFilter(srcImage.mul(srcImage), mean_II, CV_64FC1, cv::Size(r, r)); 33 // 步骤二:计算相关系数 34 cv::Mat var_I = mean_II - mean_I.mul(mean_I); 35 cv::Mat var_Ip = mean_Ip - mean_I.mul(mean_p); 36 // 步骤三:计算参数系数a,b 37 cv::Mat a = cov_Ip/(var_I + eps); 38 cv::Mat b = mean_p - a.mul(mean_I); 39 // 步骤四:计算系数a,b均值 40 cv::Mat mean_a; 41 cv::boxFilter(a, mean_a, CV_64FC1, cv::Size(r, r)); 42 mean_a = mean_a / boxResult; 43 cv::Mat mean_b; 44 cv::boxFilter(b, mean_b, CV_64FC1, cv::Size(r, r)); 45 mean_b = mean_b / boxResult; 46 //步骤五:生成输出矩阵 47 cv::Mat resultMat = mean_a.mul(srcImage) + mean_b; 48 return resultMat; 49 } 50 int main() 51 { 52 cv::Mat srcImage = cv::imread("..\images\flower3.jpg"); 53 if(srcImage.empty()) 54 return-1; 55 cv::Mat srcGray(srcImage.size(),CV_8UC1); 56 cvtColor(srcImage,srcGray,CV_BGR2GRAY); 57 // 通道分离 58 vector<Mat> vSrcImage, vResultImage; 59 split(srcImage,vSrcImage); 60 Mat resultMat; 61 for(int i=0; i < 3; i++) 62 { 63 // 分通道转换成浮点型数据 64 Mat tempImage ; 65 vSrcImage[i].convertTo(tempImage, CV_64FC1,1.0/255.0); 66 Mat p = tempImage.clone(); 67 // 分别进行导向滤波 68 Mat resultImage = guidedfilter(tempImage, p, 4, 0.01); 69 vResultImage.push_back(resultImage); 70 } 71 // 通道结果合并 72 merge(vResultImage,resultMat); 73 cv::imshow("srcImage", srcImage); 74 cv::imshow("resultMat", resultMat); 75 cv::waitKey(0); 76 return 0; 77 }
第1张图片:
参考链接:https://blog.csdn.net/gone_huilin/article/details/53223488
第2张图片:
1 //////https://blog.csdn.net/qq_34784753/article/details/70229009?locationNum=12&fps=1 2 #include <iostream> 3 #include <opencv2corecore.hpp> 4 #include <opencv2highguihighgui.hpp> 5 #include <opencv2imgprocimgproc.hpp> 6 7 using namespace cv; 8 using namespace std; 9 10 //导向滤波器 11 Mat guidedfilter(Mat &srcImage, Mat &srcClone, int r, double eps); 12 13 int main() 14 { 15 Mat srcImage = imread("D:\小狗狗.jpg"); 16 if (srcImage.empty()) 17 { 18 cout << "读入图片错误!" << endl; 19 system("pause"); 20 return -1; 21 } 22 //进行通道分离 23 //vector<Mat>vSrcImage, vResultImage; 24 Mat vSrcImage[3]; 25 Mat vResultImage[3]; 26 split(srcImage, vSrcImage); 27 Mat resultMat; 28 for (int i = 0; i < 3; i++) 29 { 30 //分通道转换成浮点型数据 31 Mat tempImage; 32 vSrcImage[i].convertTo(tempImage, CV_64FC1, 1.0 / 255.0); 33 Mat p = tempImage.clone(); 34 //分别进行导向滤波 35 Mat resultImage = guidedfilter(tempImage, p, 4, 0.01); 36 //vResultImage.push_back(resultImage); 37 vResultImage[i] = resultImage.clone(); 38 } 39 //通道结果合并 40 //merge(vResultImage, resultMat); 41 merge(vResultImage, 3, resultMat); 42 imshow("原图像", srcImage); 43 imshow("导向滤波后图像", resultMat); 44 waitKey(0); 45 return 0; 46 } 47 48 Mat guidedfilter(Mat &srcImage, Mat &srcClone, int r, double eps) 49 { 50 //转换源图像信息 51 srcImage.convertTo(srcImage, CV_64FC1); 52 srcClone.convertTo(srcClone, CV_64FC1); 53 int NumRows = srcImage.rows; 54 int NumCols = srcImage.cols; 55 Mat boxResult; 56 57 //下面按照步骤进行导向滤波操作 58 ///////////////////////////////////////////////////////////// 59 //步骤一:计算均值 60 boxFilter(Mat::ones(NumRows, NumCols, srcImage.type()), 61 boxResult, CV_64FC1, Size(r, r)); 62 //生成导向均值mean_I 63 Mat mean_I; 64 boxFilter(srcImage, mean_I, CV_64FC1, Size(r, r)); 65 //生成原始均值mean_P 66 Mat mean_P; 67 boxFilter(srcClone, mean_P, CV_64FC1, Size(r, r)); 68 //生成互相关均值mean_IP 69 Mat mean_IP; 70 boxFilter(srcImage.mul(srcClone), mean_IP, 71 CV_64FC1, Size(r, r)); 72 Mat cov_IP = mean_IP - mean_I.mul(mean_P); 73 //生成自相关均值mean_II 74 Mat mean_II; 75 //应用盒滤波计算相关均值 76 boxFilter(srcImage.mul(srcImage), mean_II, CV_64FC1, Size(r, r)); 77 //步骤二:计算相关系数 78 Mat var_I = mean_II - mean_I.mul(mean_I); 79 Mat var_IP = mean_IP - mean_I.mul(mean_P); 80 //步骤三:计算参数系数a,b 81 Mat a = cov_IP / (var_I + eps); 82 Mat b = mean_P = a.mul(mean_I); 83 //步骤四:计算系数a,b的均值 84 Mat mean_a; 85 boxFilter(a, mean_a, CV_64FC1, Size(r, r)); 86 mean_a = mean_a / boxResult; 87 Mat mean_b; 88 boxFilter(b, mean_b, CV_64FC1, Size(r, r)); 89 mean_b = mean_b / boxResult; 90 //步骤五:生成输出矩阵 91 Mat resultMat = mean_a.mul(srcImage) + mean_b; 92 return resultMat; 93 }
https://blog.csdn.net/qq_34784753/article/details/70229009?locationNum=12&fps=1
https://blog.csdn.net/kuweicai/article/details/78385871
https://blog.csdn.net/baimafujinji/article/details/74750283
https://blog.csdn.net/huixingshao/article/details/42834939
4.5.2 图像污点修复
图像修补就是使用坏点周围的像素取代坏点,这样它看起来和周围像素就比较像了。
1 //////https://blog.csdn.net/hb707934728/article/details/51980304?locationNum=8&fps=1 2 //////运行程序出现2个窗口:原始图参考和原始图,在原始图窗口绘制污点完成后,按下按键1触发修复,后显示在修补后的效果图窗口 3 //////terminal的提示非常棒! 4 #include "opencv2/highgui/highgui.hpp" 5 #include "opencv2/imgproc/imgproc.hpp" 6 #include "opencv2/photo/photo.hpp" 7 #include <iostream> 8 using namespace cv; 9 using namespace std; 10 11 12 //-----------------------------------【宏定义部分】-------------------------------------------- 13 // 描述:定义一些辅助宏 14 //---------------------------------------------------------------------------------------------- 15 #define WINDOW_NAME0 "【原始图参考】" //为窗口标题定义的宏 16 #define WINDOW_NAME1 "【原始图】" //为窗口标题定义的宏 17 #define WINDOW_NAME2 "【修补后的效果图】" //为窗口标题定义的宏 18 19 20 //-----------------------------------【全局变量声明部分】-------------------------------------- 21 // 描述:全局变量声明 22 //----------------------------------------------------------------------------------------------- 23 Mat srcImage0, srcImage1, inpaintMask; 24 Point previousPoint(-1, -1);//原来的点坐标 25 26 27 //-----------------------------------【ShowHelpText( )函数】---------------------------------- 28 // 描述:输出一些帮助信息 29 //---------------------------------------------------------------------------------------------- 30 static void ShowHelpText() 31 { 32 33 //输出一些帮助信息 34 printf(" 欢迎来到【图像修复】示例程序~ "); 35 printf(" 请在进行图像修复操作之前,在【原始图】窗口中进行适量的绘制" 36 " 按键操作说明: " 37 " 【鼠标左键】-在图像上绘制白色线条 " 38 " 键盘按键【ESC】- 退出程序 " 39 " 键盘按键【1】或【SPACE】-进行图像修复操作 "); 40 } 41 42 43 //-----------------------------------【On_Mouse( )函数】-------------------------------- 44 // 描述:响应鼠标消息的回调函数 45 //---------------------------------------------------------------------------------------------- 46 static void On_Mouse(int event, int x, int y, int flags, void*) 47 { 48 //鼠标左键弹起消息 49 if (event == CV_EVENT_LBUTTONUP || !(flags & CV_EVENT_FLAG_LBUTTON)) 50 previousPoint = Point(-1, -1); 51 //鼠标左键按下消息 52 else if (event == CV_EVENT_LBUTTONDOWN) 53 previousPoint = Point(x, y); 54 //鼠标按下并移动,进行绘制 55 else if (event == CV_EVENT_MOUSEMOVE && (flags & CV_EVENT_FLAG_LBUTTON)) 56 { 57 Point pt(x, y); 58 if (previousPoint.x < 0) 59 previousPoint = pt; 60 //绘制白色线条 61 line(inpaintMask, previousPoint, pt, Scalar::all(255), 5, 8, 0); 62 line(srcImage1, previousPoint, pt, Scalar::all(255), 5, 8, 0); 63 previousPoint = pt; 64 imshow(WINDOW_NAME1, srcImage1); 65 } 66 }
绘制污点图:
修复后图:
1 ///////https://blog.csdn.net/dcrmg/article/details/53792061 2 //////全区域阈值处理+Mask膨胀处理 3 #include <imgprocimgproc.hpp> 4 #include <highguihighgui.hpp> 5 #include <photophoto.hpp> 6 7 using namespace cv; 8 9 int main() 10 { 11 Mat imageSource = imread("D:\图像污点修复.jpg"); 12 if (!imageSource.data) 13 { 14 return -1; 15 } 16 imshow("原图", imageSource); 17 Mat imageGray; 18 //转换为灰度图 19 cvtColor(imageSource, imageGray, CV_RGB2GRAY, 0); 20 Mat imageMask = Mat(imageSource.size(), CV_8UC1, Scalar::all(0)); 21 22 //通过阈值处理生成Mask 23 threshold(imageGray, imageMask, 240, 255, CV_THRESH_BINARY); 24 Mat Kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); 25 //对Mask膨胀处理,增加Mask面积 26 dilate(imageMask, imageMask, Kernel); 27 28 //图像修复 29 inpaint(imageSource, imageMask, imageSource, 5, INPAINT_TELEA); 30 imshow("Mask", imageMask); 31 imshow("修复后", imageSource); 32 waitKey(); 33 }
原图:
处理过后:
由于是图像全区域做阈值处理获得的掩码,图像上部分区域也被当做掩码对待,导致部分图像受损。
方法二、鼠标框选区域+阈值处理+Mask膨胀处理
方法三、鼠标划定整个区域作为修复对象
参考:https://blog.csdn.net/dcrmg/article/details/53792061
https://blog.csdn.net/chuhang_zhqr/article/details/51069183
https://blog.csdn.net/qq_29540745/article/details/52563861
https://blog.csdn.net/zhangjunp3/article/details/80059769
4.5.3 旋转文本图像矫正
利用傅里叶变换中时域与频域的变换关系实现旋转文本图像矫正。
仿射变换需要获取图像的倾斜角度
旋转文本图像的明显特征是存在分行间隔,当文本图像旋转时,其频域的频谱也会随之旋转。根据这一特征来计算文本图像的DFT变换,DFT变换的结果是
霍夫变换、边缘检测等数字图像处理算法检测图像的旋转角度
1、图像DFT尺寸转换
2、DFT变换
3、频域中心移动
4、倾斜角检测
5、仿射变换
1 #include <opencv2/core/core.hpp> 2 #include <opencv2/imgproc/imgproc.hpp> 3 #include <opencv2/highgui/highgui.hpp> 4 #include <iostream> 5 using namespace cv; 6 using namespace std; 7 8 //傅里叶变化模块 9 Mat DFT(Mat srcImage) 10 { 11 //1.转换位灰度图 12 Mat srcGray; 13 cvtColor(srcImage, srcGray, CV_BGR2GRAY); 14 15 //2.将输入图像延扩到最佳的尺寸,边界用0填充 16 int nRows = getOptimalDFTSize(srcGray.rows); 17 int nCols = getOptimalDFTSize(srcGray.cols); 18 19 Mat padded; 20 //BORDER_CONSTANT是填充是常数的意思,填充的数是后面的0 21 copyMakeBorder(srcGray, padded, 0, nRows - srcGray.rows, 0, nCols - srcGray.cols, BORDER_CONSTANT, Scalar::all(0)); 22 23 //3.为傅里叶变换的结果(实部与虚部)分别存储空间 24 Mat planes[] = { Mat_<float>(padded),Mat::zeros(padded.size(),CV_32F) }; 25 Mat complexI; 26 //将planes数组组合合并成一个多通道的数组complexI 27 merge(planes, 2, complexI);//将两个数组合并成一个多通道的数组 28 29 //4.进行离散傅里叶变换 30 dft(complexI, complexI); 31 //5.将复数转化为幅值 32 // planes[0] = Re(DFT(I), planes[1] = Im(DFT(I) 33 split(complexI, planes);//将多通道数组complexI分解为几个单通道的数组 34 //magnitude(x,y,dst);dst=sqrt(x(I)^2+y(I)^2); 35 magnitude(planes[0], planes[1], planes[0]); 36 Mat magnitudeImage = planes[0].clone();//傅里叶变换的幅值矩阵 37 //6.进行对数尺度缩放 38 magnitudeImage += Scalar::all(1);//对幅值都加1 39 log(magnitudeImage, magnitudeImage); 40 //归一化,用0到1之间的浮点值将矩阵变换为可视的图像格式 41 normalize(magnitudeImage, magnitudeImage, 0, 1, CV_MINMAX); 42 //图像类型转换,为了下一步的霍夫直线检测做准备 43 magnitudeImage.convertTo(magnitudeImage, CV_8UC1, 255, 0); 44 imshow("magnitudeImage", magnitudeImage); 45 //7.剪切与重分布幅度图象限 46 //若有奇数行或者奇数列,进行频谱裁剪 47 //magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols & -2, magnitudeImage.rows & -2)); 48 //重新排列傅里叶图像中的象限,使得原点位于图像中心 49 int cx = magnitudeImage.cols / 2; 50 int cy = magnitudeImage.rows / 2; 51 Mat q0(magnitudeImage, Rect(0, 0, cx, cy));//ROI区域的左上 52 Mat q1(magnitudeImage, Rect(cx, 0, cx, cy));//ROI区域的右上 53 Mat q2(magnitudeImage, Rect(0, cy, cx, cy));//ROI区域的左下 54 Mat q3(magnitudeImage, Rect(cx, cy, cx, cy));//ROI区域的右上 55 56 //交换象限(左上与右下进行交换) 57 Mat temp; 58 q0.copyTo(temp); 59 q3.copyTo(q0); 60 temp.copyTo(q3); 61 //交换象限(右上与左下进行交换) 62 q1.copyTo(temp); 63 q2.copyTo(q1); 64 temp.copyTo(q2); 65 Point pt1, pt2; 66 pt1.x = cx; 67 pt2.x = cx; 68 pt1.y = 0; 69 pt2.y = magnitudeImage.rows; 70 71 //往书上结果靠拢,去掉频域中心移动的横线和竖线,后面判断直线角度直接不需要 72 //line(magnitudeImage, pt1, pt2, Scalar(0, 0, 0), 1, CV_AA); 73 74 return magnitudeImage; 75 } 76 //倾斜角检测 返回检测到的倾斜角 77 float AngleDetection(Mat srcImage) 78 { 79 int nRows = srcImage.rows; 80 int nCols = srcImage.cols; 81 Mat binaryMat; 82 //对傅里叶变换之后的图像进行二值化处理 83 //根据不同的图片情况可能需要调整这个固定阈值 84 threshold(srcImage, binaryMat, 135, 255, CV_THRESH_BINARY); 85 imshow("binaryMat", binaryMat);//显示二值图像 86 //霍夫变换 87 vector<Vec2f> lines; 88 binaryMat.convertTo(binaryMat, CV_8UC1, 255, 0); 89 //下面阈值可能也需要根据情况调整 90 HoughLines(binaryMat, lines, 0.5, CV_PI / 180, 30, 0, 0); 91 //检测线的个数 92 cout << "lines.size:" << lines.size() << endl; 93 //创建一张直线检测图形 94 Mat houghMat(binaryMat.size(), CV_8UC3); 95 //绘制线检测 96 for (size_t i = 0; i < lines.size(); i++) 97 { 98 //根据直线参数表达式绘制相应的检测结果 99 float rho = lines[i][0], theta = lines[i][1]; 100 Point pt1, pt2; 101 double a = cos(theta), b = sin(theta); 102 double x0 = a*rho, y0 = b*rho; 103 pt1.x = cvRound(x0 + 1000 * (-b)); 104 pt1.y = cvRound(y0 + 1000 * (a)); 105 pt2.x = cvRound(x0 - 1000 * (-b)); 106 pt2.y = cvRound(y0 - 1000 * (a)); 107 line(houghMat, pt1, pt2, Scalar(255, 0, 0), 3, CV_AA); 108 } 109 //显示绘制的图 110 imshow("houghMat", houghMat); 111 112 float theta2 = 0; 113 //检测线角度判断 114 //因为文本检测的时候有三条直线,一条水平,一条垂直,还有一条就是我们需要的直线 115 for (size_t i = 0; i < lines.size(); i++) 116 { 117 float ThetaTemp = lines[i][1] * 180 / CV_PI; 118 //找出我们需要的那条直线就退出循环 119 if (ThetaTemp > 0 && ThetaTemp < 90) 120 { 121 theta2 = ThetaTemp; 122 break; 123 } 124 } 125 //角度转换 126 float angelT = nRows*tan(theta2 / 180 * CV_PI) / nCols; 127 theta2 = atan(angelT) * 180 / CV_PI; 128 cout << "theta2:" << theta2 << endl; 129 return theta2; 130 } 131 132 //旋转图像,可以使用仿射变换 133 Mat RotIamge(Mat &srcImage, float Angle) //旋转角度然后返回一张旋转后的图像 134 { 135 //以图像的中心为原点求出原图中的四个角的坐标 136 float SrcX1, SrcY1, SrcX2, SrcY2, SrcX3, SrcY3, SrcX4, SrcY4; 137 //新图中四个角的坐标 138 float DstX1, DstY1, DstX2, DstY2, DstX3, DstY3, DstX4, DstY4; 139 //将角度转换位弧度 140 float alpha = Angle*CV_PI / 180; 141 //原图的宽高 142 int Wold = srcImage.cols; 143 int Hold = srcImage.rows; 144 145 //假设原图的原点在原图的中心主要是为了求出新图的宽高 146 //原图的四个角的坐标 147 SrcX1 = (float)((-0.5)*Wold); 148 SrcY1 = (float)((0.5)*Hold); 149 SrcX2 = (float)((0.5)*Wold); 150 SrcY2 = (float)((0.5)*Hold); 151 SrcX3 = (float)((-0.5)*Wold); 152 SrcY3 = (float)((-0.5)*Hold); 153 SrcX4 = (float)((0.5)*Wold); 154 SrcY4 = (float)((-0.5)*Hold); 155 //求出cosa 和sina 156 float cosa = cos(alpha); 157 float sina = sin(alpha); 158 159 //求出新图的四个角的坐标 160 DstX1 = cosa*SrcX1 + sina*SrcY1; 161 DstY1 = -sina*SrcX1 + cosa*SrcY1; 162 DstX2 = cosa*SrcX2 + sina*SrcY2; 163 DstY2 = -sina*SrcX2 + cosa*SrcY2; 164 DstX3 = cosa*SrcX3 + sina*SrcY3; 165 DstY3 = -sina*SrcX3 + cosa*SrcY3; 166 DstX4 = cosa*SrcX4 + sina*SrcY4; 167 DstY4 = -sina*SrcX4 + cosa*SrcY4; 168 169 //计算新图的宽和高 170 int Wnew = cvRound(max(abs(DstX4 - DstX1), abs(DstX3 - DstX2))); 171 int Hnew = cvRound(max(abs(DstY4 - DstY1), abs(DstY3 - DstY2))); 172 173 //计算矩阵中的两个常数 174 float num1 = (float)(-0.5*Wnew*cosa - 0.5*Hnew*sina + 0.5*Wold); 175 float num2 = (float)(0.5*Wnew*sina - 0.5*Hnew*cosa + 0.5*Hold); 176 //创建一张新的大小的图 177 Mat resultImage(Hnew, Wnew, srcImage.type()); 178 for (int i = 0; i < Hnew; i++) //rows 179 { 180 for (int j = 0; j < Wnew; j++)//cols 181 { 182 //求出新图中的像素在原图的位置 183 int x = cvRound(i*cosa + j*sina + num1); 184 int y = cvRound(-sina*i + j*cosa + num2); 185 if ((x >= 0) && (x < Wold) && (y >= 0) && (y < Hold))//在原图中的范围 186 { 187 resultImage.at<Vec3b>(i, j) = srcImage.at<Vec3b>(x, y); 188 } 189 } 190 } 191 return resultImage; 192 } 193 int main() 194 { 195 196 Mat srcImage = imread("D:\倾斜文本.jpg"); 197 if (!srcImage.data) 198 { 199 printf("could not load image... "); 200 return -1; 201 } 202 //Point center(srcImage.cols / 2, srcImage.rows / 2); 203 //srcImage = getRotationMatrix2D(center, 27, 1.0); 204 imshow("srcImage", srcImage); 205 //输出傅里叶变换之后的图形 206 Mat DFTImage = DFT(srcImage); 207 imshow("DFT", DFTImage); 208 //求出傅里叶变换里面的倾斜角 209 float theta = AngleDetection(DFTImage); 210 Mat retultImage = RotIamge(srcImage, theta); 211 imshow("res", retultImage); 212 waitKey(0); 213 return 0; 214 }
参考:
https://blog.csdn.net/linqianbi/article/details/78863839
https://www.cnblogs.com/skyfsm/p/6902524.html
https://blog.csdn.net/liumoude6/article/details/77688798
http://lib.csdn.net/article/opencv/24201
https://blog.csdn.net/longwinyang/article/details/52789260
https://blog.csdn.net/liyuanbhu/article/details/50099767