数字图像处理21--OpenCV混合空间增强法 C++

前面图像处理主要使用单一增强法;为达到令人满意的效果,对于给定任务需要采用多种互补的图像增强技术。
使用教材上的人体骨骼图片实验;目的是通过图像锐化突出骨骼的更多细节来增强图像。
由于灰度图像的动态范围很窄并且有很高的噪声内容,所以很难对其进行增强。
对此采取的策略是:


首先用拉普拉斯法(Laplace)突出图像中的小细节img1,-->梯度法(Sobel)突出其边缘img2,
平滑(blur)过的梯度图img2将用于掩蔽拉普拉斯图像img1--->使用灰度(gama)变换来增大图像的动态范围

原始图像a:                                                         图像b,原图像使用拉普拉斯算子(1,1,1,1,-8,1,1,1,1)标定后的图像

                     

图像c, 由图像a和图像b相加得到的结果,图b噪声就很大,导致图c噪声也很大;降低噪声可选中值滤波;但中值滤波是非线性滤波,可能改变图像性质。所以医学上不可接受。

另一种方法是使用原图像梯度操作的平滑形式所形成的模板。

因为拉普拉斯算子是二阶微分,在增强细节方面他是最好的。但它会产生比梯度操作更多的噪音。尤其在平滑区域,非常明显。

梯度变换在灰度变化的区域(斜坡或台阶)的平均响应要比拉普拉斯操作的平均响应更强烈。梯度操作对噪声和小细节的响应比拉普拉斯弱。而且可以通过均值滤波对其平滑处理。

思路是对梯度图像进行平滑处理并用拉普拉斯图像与他相乘。

平滑后的梯度图像可以看成模板图像。

相乘会保留灰度变化强烈区域的细节,同时降低灰度变化平坦区域的噪音。将拉普拉斯和梯度操作的优点相结合。

将结果加到原图像得到最终的锐化图像。

图像d:图a经sobel算子处理后的结果,该图像的边缘比拉普拉斯清晰很多

图像e:使用5x5均值滤波过的Sobel图像                                     图像f:由c和e相乘得到的掩蔽图像

                                          

然后相加原图a与掩蔽f相加得到锐化图像g, 伽马变换得到图像h

图像g:                                                                                       图像h:

                                        

说明:

图d,图e比图b亮,再次说明了具有重要边缘内容的梯度图像的值一般比拉普拉斯高。

代码实现如下:

#include<opencv2/opencv.hpp>
#include<iostream>

using namespace std;
using namespace cv;

//标定单通道的图像
void calibration_one_channel(Mat &input, Mat &output)
{
    Mat dst;
    double max = 0, min = 0;
    dst = Mat::zeros(input.rows, input.cols, CV_8UC1);
    minMaxLoc( input, &min, &max);  //取出图像中的上下限

    for (int i = 0; i<input.rows; i++){
        float *ptr = input.ptr<float>(i);
        uchar *optr = dst.ptr<uchar>(i);
        for (int j = 0; j<input.cols; j++){
            optr[j] = (uchar)((ptr[j] - min) / (max - min) * 255);
        }
    }
    if( !output.empty() ) output.release();
    output = dst.clone();
}

void toBeOne(Mat &input, Mat &output, int index = 0)
{
    if( input.empty() ){
        std::cerr << "input image empty" << std::endl;
        return;
    }

    if( input.channels() == 3 ){
        //split image to 3 channels
        std::vector<cv::Mat> channels;
        cv::split( input, channels );
        for( int k = 0; k < input.channels(); k++){
            if( index){
                normalize(channels[k], channels[k], 0, 255, NORM_MINMAX);//归一化处理
                convertScaleAbs(channels[k], channels[k]);
            }else {
                calibration_one_channel(channels[k], channels[k]);
            }
        }
        cv::merge( channels, output );
    }else if( input.channels() == 1) {
        if(index){
            normalize(input, input, 0, 255, NORM_MINMAX);//归一化处理
            convertScaleAbs(input, output);
        }else {
            calibration_one_channel(input, output);
        }
    }
}

void toBeOne2(Mat &input, Mat &output, int index = 0)
{
    double max = 0, min = 0;
    output = Mat::zeros(input.rows, input.cols, CV_8UC3);
    minMaxIdx(input, &min, &max);    //取出图像中的上下限
    for (int i = 0; i<input.rows; i++){
        float *ptr = input.ptr<float>(i);
        uchar *optr = output.ptr<uchar>(i);
        for (int j = 0; j<input.cols * 3; j++){
            if (index){
                if (ptr[j]>1){
                    optr[j] = 255;
                    continue;
                }
                else if (ptr[j]<0){
                    optr[j] = 0;
                    continue;
                }
                else
                    optr[j] = (uchar)(ptr[j] * 255);
            }
            else
                optr[j] = (uchar)((ptr[j] - min) / (max - min) * 255);      //标定image
        }
    }
}

//γ变换
cv::Mat gammaTran(const cv::Mat src, double gamma, double comp)
{
    cv::Mat dst(src);
    int M = 0;
    int N = 0;
    if (src.empty()){
        std::cout << "Src pic is empty" << std::endl;
        return src;
    }
    M = src.rows;
    N = src.cols*src.channels();
    for (int i = 0; i < M; i++){
        const float *p1 = src.ptr<const float>(i);
        float *p2 = dst.ptr<float>(i);
        for (int j = 0; j < N; j++){
            p2[j] = pow(p1[j], gamma) * comp;
        }
    }
    return dst;
}

int main( int argc, char *argv[])
{
    if( argc != 2 ){
        std::cerr << "Usage: " << argv[0] << " <image_name>" << std::endl;
        return 0;
    }
    std::string image_name = argv[1];
    Mat iinput = imread(image_name, IMREAD_COLOR), input, Tlaplas;
    imshow("original", iinput);
    iinput.convertTo(input, CV_32F, 1.0 / 255, 0);//把图片转化为float类型,这样子可以直接进行加减而不会溢出

    //Laplacian变换
    Mat kern = (Mat_<float>(3, 3) << 1, 1, 1,       //滤波器
        1, -8, 1,
        1, 1, 1);
    Mat laplas;
    Mat output;
    filter2D(input, laplas, input.depth(), kern);//使用滤波器kern对input进行相关操作,结果存储在laplas中
    toBeOne(laplas, Tlaplas);
    imshow("filter2D_one", Tlaplas);

    output = input - laplas;//如果中间的值是正的则是加号,负值则是减号
    toBeOne(output, iinput, 1);
    Mat R0 = iinput;
    imshow("direct_laplace", R0);

    //Sobel梯度
    Mat kern2 = (Mat_<float>(3, 3) << -1, -2, -1,
        0, 0, 0,
        1, 2, 1);
    Mat kern3 = (Mat_<float>(3, 3) << -1, 0, 1,
        -2, 0, 2,
        -1, 0, 1);
    Mat gx, gy;
    filter2D(input, gx, input.depth(), kern2);
    filter2D(input, gy, input.depth(), kern3);
    Mat Soutput = abs(gx) + abs(gy);
    toBeOne(Soutput, iinput, 1);
    Mat R1 = iinput;
    imshow("Sobel1", R1);

    Mat smooth;
    blur(iinput, smooth, Size(5, 5));
    Mat R2 = smooth;
    imshow("Smooth", R2);

    //相乘
    Mat R3, r3;
    R0.convertTo(R0, CV_32F, 1.0 / 255, 0);
    R2.convertTo(R2, CV_32F, 1.0 / 255, 0);
    multiply(R0, R2, r3);
    toBeOne(r3, R3, 1);
    imshow("Result0",R3);


    //锐化增强
    cv::Mat r4, R4;
    r4 = input + r3;
    toBeOne(r4, R4, 1);
    imshow("Result1", R4);

    //γ变换
    cv::Mat r5, R5;
    r5 = gammaTran(r4, 0.5, 1);
    toBeOne(r5, R5, 1);
    imshow("Result", R5);
    imwrite( "8.jpg", R5);

    waitKey();
    return 0;
}

下图是当做灰度图像处理的,结果不同

  

猜你喜欢

转载自blog.csdn.net/cyf15238622067/article/details/87874351