灰度图像的对数变换原理及OpenCV代码实现!

男娘i 2022-08-23 09:54 304阅读 0赞

对数变换的公式为:

20160613153019991

其中c为常数,r>=0

对数变换目前我知道的有两个作用:

①因为对数曲线在像素值较低的区域斜率较大,像素值较高的区域斜率比较低,所以图像经过对数变换之后,在较暗的区域对比度将得到提升,因而能增强图像暗部的细节。

②图像的傅里叶频谱其动态范围可能宽达0~10^6。直接显示频谱的话显示设备的动态范围往往不能满足要求,这个时候就需要使用对数变换,使得傅里叶频谱的动态范围被合理地非线性压缩。

在OpenCV中,图像对数变换的实现可以直接通过对图像中每个元素运算上述公式完成,也可以通过矩阵整体操作来完成。下面的代码中给了三种方法,其中方法一和方法三都是通过矩阵整体操作来完成,第二种方法是对图像中每个元素操作来完成。方法一和方法三的区别是前者是对源图像作对数运算,后者是对目标图像作对数运算!

源代码如下(代码中用到的图像下载链接 http://pan.baidu.com/s/1i4Dvm2h):

图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号”qxsf321”,并关注!

  1. //OpenCV版本2.4.9
  2. //图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!
  3. #include <opencv2/core/core.hpp>
  4. #include <opencv2/highgui/highgui.hpp>
  5. #include <opencv2/imgproc/imgproc.hpp>
  6. #include <iostream>
  7. using namespace cv;
  8. // 对数变换方法1
  9. cv::Mat logTransform1(cv::Mat srcImage, int c)
  10. {
  11. // 输入图像判断
  12. if(srcImage.empty())
  13. std::cout<< "No data!" <<std::endl;
  14. cv::Mat resultImage =
  15. cv::Mat::zeros(srcImage.size(), srcImage.type());
  16. // 计算 1 + r
  17. cv::add(srcImage, cv::Scalar(1.0), srcImage);
  18. // 转换为32位浮点数
  19. srcImage.convertTo(srcImage, CV_32F);
  20. // 计算 log(1 + r)
  21. log(srcImage, resultImage);
  22. resultImage = c * resultImage;
  23. // 归一化处理
  24. cv::normalize(resultImage,resultImage,
  25. 0,255,cv::NORM_MINMAX);
  26. cv::convertScaleAbs(resultImage,resultImage);
  27. return resultImage;
  28. }
  29. // 对数变换方法2
  30. cv::Mat logTransform2(Mat srcImage,float c)
  31. {
  32. // 输入图像判断
  33. if(srcImage.empty())
  34. std::cout<< "No data!" <<std::endl;
  35. cv::Mat resultImage =
  36. cv::Mat::zeros(srcImage.size(), srcImage.type());
  37. double gray = 0;
  38. // 图像遍历分别计算每个像素点的对数变换
  39. for(int i = 0;i < srcImage.rows;i++){
  40. for(int j = 0; j < srcImage.cols; j++){
  41. gray = (double)srcImage.at<uchar>(i,j);
  42. gray = c*log((double)(1 + gray));
  43. resultImage.at<uchar>(i,j)=saturate_cast<uchar>(gray);
  44. }
  45. }
  46. // 归一化处理
  47. cv::normalize(resultImage,resultImage,
  48. 0,255,cv::NORM_MINMAX);
  49. cv::convertScaleAbs(resultImage,resultImage);
  50. return resultImage;
  51. }
  52. // 对数变换方法3
  53. cv::Mat logTransform3(Mat srcImage, float c)
  54. {
  55. // 输入图像判断
  56. if(srcImage.empty())
  57. std::cout<< "No data!" <<std::endl;
  58. cv::Mat resultImage =
  59. cv::Mat::zeros(srcImage.size(), srcImage.type());
  60. srcImage.convertTo(resultImage,CV_32F);
  61. resultImage = resultImage + 1;
  62. cv::log(resultImage,resultImage);
  63. resultImage = c * resultImage;
  64. cv::normalize(resultImage,resultImage,0,255,cv::NORM_MINMAX);
  65. cv::convertScaleAbs(resultImage,resultImage);
  66. return resultImage;
  67. }
  68. int main()
  69. {
  70. // 读取灰度图像及验证
  71. cv::Mat srcImage = cv::imread("lakeWater.jpg",0);
  72. if(!srcImage.data)
  73. return -1;
  74. // 验证三种不同方式的对数变换速度
  75. cv::imshow("srcImage", srcImage);
  76. float c = 1.2;
  77. cv::Mat resultImage;
  78. double tTime;
  79. tTime = (double)getTickCount();
  80. const int nTimes = 10;
  81. for(int i = 0; i < nTimes; i++)
  82. {
  83. resultImage = logTransform1(srcImage, c);
  84. }
  85. tTime = 1000*((double)getTickCount() - tTime)/
  86. getTickFrequency();
  87. tTime /= nTimes;
  88. std::cout << "第一种方法耗时:"<<tTime << std::endl;
  89. cv::imshow("resultImage", resultImage);
  90. cv::waitKey(0);
  91. return 0;
  92. }

运行结果如下图所示:

20160613160951537

201606131610394902016061316104756920160613161055162

可见,第二种方法将来在做具体应用的时候是肯定要放弃的!

发表评论

表情:
评论列表 (有 0 条评论,304人围观)

还没有评论,来说两句吧...

相关阅读