1 ////https://blog.csdn.net/uestc_c2_403/article/details/72814206 2 #include "opencv2/highgui/highgui.hpp" 3 #include "opencv2/imgproc/imgproc.hpp" 4 #include "opencv2/opencv.hpp" 5 #include "opencv2/core/core.hpp" 6 #include7 using namespace std; 8 using namespace cv; 9 //图像线性变换操作10 cv::Mat linearTransform(cv::Mat srcImage, float a, int b)11 {12 if (srcImage.empty())13 {14 std::cout << "No data!" << std::endl;15 }16 const int nRows = srcImage.rows;17 const int nCols = srcImage.cols;18 cv:; Mat resultImage =19 cv::Mat::zeros(srcImage.size(), srcImage.type());20 //图像元素遍历21 for (int i = 0; i < nRows; i++)22 {23 for (int j = 0; j < nCols; j++)24 {25 for (int c = 0; c < 3; c++)26 {27 //矩阵at操作,检查下标防止越界28 resultImage.at (i, j)[c] = saturate_cast (a * (srcImage.at (i, j)[c]) + b);29 }30 }31 }32 return resultImage;33 }34 int main()35 {36 cv::Mat srcImage = cv::imread("D:\\大海.jpg");37 if (srcImage.empty())38 {39 return -1;40 }41 42 cv::imshow("源图像", srcImage);43 //线性变换44 float a = 1.2;45 int b = 50;46 cv::Mat new_image = linearTransform(srcImage, a, b);47 cv::imshow("dst", new_image);48 cv::waitKey(0);49 return 0;50 }
3.5.2 对数变换
对数变换的公式为:
其中c为常数,r>=0
对数变换目前我知道的有两个作用:
①因为对数曲线在像素值较低的区域斜率较大,像素值较高的区域斜率比较低,所以图像经过对数变换之后,在较暗的区域对比度将得到提升,因而能增强图像暗部的细节。
②图像的傅里叶频谱其动态范围可能宽达0~10^6。直接显示频谱的话显示设备的动态范围往往不能满足要求,这个时候就需要使用对数变换,使得傅里叶频谱的动态范围被合理地非线性压缩。
在OpenCV中,图像对数变换的实现可以直接通过对图像中每个元素运算上述公式完成,也可以通过矩阵整体操作来完成。下面的代码中给了三种方法,其中方法一和方法三都是通过矩阵整体操作来完成,第二种方法是对图像中每个元素操作来完成。方法一和方法三的区别是前者是对源图像作对数运算,后者是对目标图像作对数运算!
1 //https://blog.csdn.net/gone_huilin/article/details/53223118 2 //https://blog.csdn.net/spw_1201/article/details/53482877 3 #include4 #include 5 #include 6 #include 7 using namespace cv; 8 using namespace std; 9 //基于源图像的方法110 Mat logTransform1(Mat srcImage, int c)11 {12 if (srcImage.empty())13 cout << "No data" << endl;14 Mat resultImage = Mat::zeros(srcImage.size(), srcImage.type());15 add(srcImage, Scalar(1.0), srcImage); //计算 r+116 srcImage.convertTo(srcImage, CV_32F); //转化为32位浮点型17 log(srcImage, resultImage); //计算log(1+r)18 resultImage = c*resultImage;19 //归一化处理20 normalize(resultImage, resultImage, 0, 255, NORM_MINMAX);21 convertScaleAbs(resultImage, resultImage);22 return resultImage;23 24 }25 //方法226 Mat logTransform2(Mat srcImage, float c)27 {28 if (srcImage.empty())29 cout << "No data" << endl;30 Mat resultImage = Mat::zeros(srcImage.size(), srcImage.type());31 double gray = 0;32 for (int i = 0; i < srcImage.rows; i++)33 {34 for (int j = 0; j < srcImage.cols; j++)35 {36 gray = (double)srcImage.at (i, j);37 gray = c*log((double)(1 + gray));38 resultImage.at (i, j) = saturate_cast (gray);39 }40 }41 //归一化处理42 normalize(resultImage, resultImage, 0, 255, NORM_MINMAX);43 convertScaleAbs(resultImage, resultImage);44 return resultImage;45 }46 //方法347 Mat logTransform3(Mat srcImage, float c)48 {49 if (srcImage.empty())50 cout << "No data" << endl;51 Mat resultImage = Mat::zeros(srcImage.size(), srcImage.type());52 srcImage.convertTo(resultImage, CV_32F); //图像类型转换53 resultImage = resultImage + 1; //图像矩阵元素加154 log(resultImage, resultImage);55 resultImage = c*resultImage;56 //归一化处理57 normalize(resultImage, resultImage, 0, 255, NORM_MINMAX);58 convertScaleAbs(resultImage, resultImage);59 return resultImage;60 }61 int main()62 {63 Mat srcImage = imread("D:\\大海.jpg");64 if (!srcImage.data)65 return -1;66 cvtColor(srcImage, srcImage, CV_BGR2GRAY);67 imshow("srcImage", srcImage);68 float c = 1.2;69 Mat resultImage;70 double tTime;71 tTime = (double)getTickCount();72 const int nTimes = 100;73 for (int i = 0; i < nTimes; i++)74 {75 resultImage = logTransform3(srcImage, c);76 }77 tTime = 1000 * ((double)getTickCount() - tTime) / getTickFrequency();78 tTime /= nTimes;79 cout << tTime << endl;80 imshow("resultImage", resultImage);81 waitKey(0);82 return 0;83 }
参考:
3.5.3 对比度拉伸
1 //https://blog.csdn.net/spw_1201/article/details/53486594 2 #include3 #include 4 #include 5 #include 6 using namespace cv; 7 using namespace std; 8 Mat contrastStretch(Mat srcImage) 9 {10 Mat resultImage = srcImage.clone();//"=";"clone()";"copyTo"三种拷贝方式,前者是浅拷贝,后两者是深拷贝。11 int nRows = resultImage.rows;12 int nCols = resultImage.cols;13 //判断图像的连续性14 if (resultImage.isContinuous())15 {16 nCols = nCols*nRows;17 nRows = 1;18 }19 //图像指针操作20 uchar *pDataMat;21 int pixMax = 0, pixMin = 255;22 //计算图像的最大最小值23 for (int j = 0; j < nRows; j++)24 {25 pDataMat = resultImage.ptr (j);//ptr<>()得到的是一行指针26 for (int i = 0; i < nCols; i++)27 {28 if (pDataMat[i] > pixMax)29 pixMax = pDataMat[i];30 if (pDataMat[i] < pixMin)31 pixMin = pDataMat[i];32 }33 }34 //对比度拉伸映射35 for (int j = 0; j < nRows; j++)36 {37 pDataMat = resultImage.ptr (j);38 for (int i = 0; i < nCols; i++)39 {40 pDataMat[i] = (pDataMat[i] - pixMin) * 255 / (pixMax - pixMin);41 }42 }43 return resultImage;44 }45 int main()46 {47 Mat srcImage = imread("D:\\小女孩与熊.jpg");48 if (!srcImage.data)49 return -1;50 Mat srcGray;51 cvtColor(srcImage, srcGray, CV_RGB2GRAY);52 imshow("srcGray", srcGray);53 Mat resultImage = contrastStretch(srcGray);54 imshow("resultImage", resultImage);55 waitKey(0);56 return 0;57 }
3.5.4 灰度级分层
1 //https://blog.csdn.net/spw_1201/article/details/53487746 2 #include3 #include 4 #include 5 #include 6 using namespace cv; 7 using namespace std; 8 Mat grayLayered(Mat srcImage) 9 {10 Mat resultImage = srcImage.clone();11 int nRows = resultImage.rows;12 int nCols = resultImage.cols;13 if (resultImage.isContinuous())14 {15 nCols = nRows*nCols;16 nRows = 1;17 }18 uchar *pDataMat;19 int controlMin = 150;20 int controlMax = 200;21 for (int j = 0; j < nRows; j++)22 {23 pDataMat = resultImage.ptr (j);24 for (int i = 0; i < nCols; i++)25 {26 //第一种方法:二值映射27 if (pDataMat[i] > controlMin)28 pDataMat[i] = 255;29 else30 pDataMat[i] = 0;31 //第二种方法:区域映射32 /*if (pDataMat[i] > controlMin && pDataMat[i] < controlMax)33 pDataMat[i] = controlMax;*/34 }35 }36 return resultImage;37 }38 int main()39 {40 Mat srcImage = imread("D:\\大海.jpg");41 if (!srcImage.data)42 return -1;43 Mat srcGray;44 cvtColor(srcImage, srcGray, CV_BGR2GRAY);45 imshow("srcGray", srcGray);46 Mat resultImage = grayLayered(srcGray);47 imshow("resultImage", resultImage);48 waitKey(0);49 return 0;50 }
3.5.5 灰度比特平面
1 //https://blog.csdn.net/gone_huilin/article/details/53223151 2 #include3 #include 4 #include 5 #include 6 #include 7 using namespace cv; 8 using namespace std; 9 void showMBitPlan(cv::Mat srcImage)10 {11 int nRows = srcImage.rows;12 int nCols = srcImage.cols;13 // 图像连续性判断14 if (srcImage.isContinuous())15 {16 nCols = nCols * nRows;17 nRows = 1;18 }19 // 图像指针操作20 uchar *pSrcMat;21 uchar *pResultMat;22 cv::Mat resultImage = srcImage.clone();23 int pixMax = 0, pixMin = 0;24 for (int n = 1; n <= 8; n++)25 {26 // 比特平面分层像素构成27 pixMin = pow(2, n - 1);28 pixMax = pow(2, n);29 for (int j = 0; j < nRows; j++)30 {31 // 获取图像数据指针32 pSrcMat = srcImage.ptr (j);33 pResultMat = resultImage.ptr (j);34 for (int i = 0; i < nCols; i++)35 {36 // 相应比特平面层二值化37 if (pSrcMat[i] >= pixMin && pSrcMat[i] < pixMax)38 pResultMat[i] = 255;39 else40 pResultMat[i] = 0;41 }42 }43 // 比特平面层输出 44 char windowsName[20];45 sprintf_s(windowsName, "BitPlane %d", n);46 imshow(windowsName, resultImage);47 }48 }49 int main()50 {51 cv::Mat srcImage = cv::imread("D:\\大海.jpg");52 if (!srcImage.data)53 return 0;54 cv::Mat srcGray;55 cvtColor(srcImage, srcGray, CV_BGR2GRAY);56 imshow("srcGray", srcGray);57 showMBitPlan(srcGray);58 cv::waitKey(0);59 return 0;60 }