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

图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601

对数变换的公式为:

其中c为常数,r>=0 

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

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

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

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

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

//OpenCV版本2.4.9  
//图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
//图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601

#include <opencv2/core/core.hpp>        
#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgproc/imgproc.hpp>  
#include <iostream> 
using namespace cv;
// 对数变换方法1
cv::Mat logTransform1(cv::Mat srcImage, int c)
{
    // 输入图像判断
    if(srcImage.empty())
        std::cout<< "No data!" <<std::endl;  
    cv::Mat resultImage = 
      cv::Mat::zeros(srcImage.size(), srcImage.type());
    // 计算 1 + r
    cv::add(srcImage, cv::Scalar(1.0), srcImage);
    // 转换为32位浮点数
    srcImage.convertTo(srcImage, CV_32F); 
    // 计算 log(1 + r)
    log(srcImage, resultImage);
    resultImage = c * resultImage;
    // 归一化处理
    cv::normalize(resultImage,resultImage,
        0,255,cv::NORM_MINMAX);
    cv::convertScaleAbs(resultImage,resultImage);
    return resultImage;
}
// 对数变换方法2
cv::Mat logTransform2(Mat srcImage,float c)  
{  
    // 输入图像判断
    if(srcImage.empty())
        std::cout<< "No data!" <<std::endl;
    cv::Mat resultImage = 
      cv::Mat::zeros(srcImage.size(), srcImage.type());  
    double gray = 0;
    // 图像遍历分别计算每个像素点的对数变换  
    for(int i = 0;i < srcImage.rows;i++){  
        for(int j = 0; j < srcImage.cols; j++){  
            gray = (double)srcImage.at<uchar>(i,j); 
            gray = c*log((double)(1 + gray));  
            resultImage.at<uchar>(i,j)=saturate_cast<uchar>(gray); 
        }  
    }
    // 归一化处理
    cv::normalize(resultImage,resultImage,
        0,255,cv::NORM_MINMAX);
    cv::convertScaleAbs(resultImage,resultImage);
    return resultImage; 
}
// 对数变换方法3
cv::Mat logTransform3(Mat srcImage, float c)  
{  
    // 输入图像判断
    if(srcImage.empty())
        std::cout<< "No data!" <<std::endl;
    cv::Mat resultImage = 
      cv::Mat::zeros(srcImage.size(), srcImage.type());  
    srcImage.convertTo(resultImage,CV_32F);
    resultImage = resultImage + 1;
    cv::log(resultImage,resultImage);
    resultImage = c * resultImage;
    cv::normalize(resultImage,resultImage,0,255,cv::NORM_MINMAX);
    cv::convertScaleAbs(resultImage,resultImage);
    return resultImage; 
}
int main()
{
    // 读取灰度图像及验证
    cv::Mat srcImage = cv::imread("lakeWater.jpg",0); 
    if(!srcImage.data) 
        return -1;
    // 验证三种不同方式的对数变换速度
    cv::imshow("srcImage", srcImage); 
    float c = 1.2;
    cv::Mat resultImage;
    double tTime;
    tTime = (double)getTickCount();
    const int nTimes = 10;
    for(int i  = 0;  i < nTimes; i++)
    {
        resultImage = logTransform1(srcImage, c); 
    }
    tTime = 1000*((double)getTickCount() - tTime)/
    getTickFrequency();
    tTime /= nTimes;
    std::cout << "第一种方法耗时:"<<tTime << std::endl;   
    cv::imshow("resultImage", resultImage);
    cv::waitKey(0);
    return 0;
}

运行结果如下图所示:

可见,第二种方法将来在做具体应用的时候是肯定要放弃的!
图像处理开发需求、图像处理接私活挣零花钱,请加微信/QQ 2487872782
图像处理开发资料、图像处理技术交流请加QQ群,群号 271891601

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值