使用拉普拉斯滤波器进行图像锐化

10

我试图对来自Gonzalez书籍的一些标准图像进行锐化。下面是我尝试过的一些代码,但它们并不能接近锐化后的图片结果。

cvSmooth(grayImg, grayImg, CV_GAUSSIAN, 3, 0, 0, 0);

IplImage* laplaceImg = cvCreateImage(cvGetSize(oriImg), IPL_DEPTH_16S, 1);

IplImage* abs_laplaceImg = cvCreateImage(cvGetSize(oriImg), IPL_DEPTH_8U, 1);

cvLaplace(grayImg, laplaceImg, 3);

cvConvertScaleAbs(laplaceImg, abs_laplaceImg, 1, 0);

IplImage* dstImg = cvCreateImage(cvGetSize(oriImg), IPL_DEPTH_8U, 1);
cvAdd(abs_laplaceImg, grayImg, dstImg, NULL); 

Before Sharpening 未锐化之前的图像

My Sharpening Result 我的锐化结果

Desired Result 期望的锐化结果

Absolute Laplace 绝对拉普拉斯算子


你尝试过什么?你尝试过调整Laplacian核的大小吗?你尝试在更模糊的图像上应用cvLaplace()吗?你尝试过使用cvAddWeighted()替代cvAdd()吗(似乎你需要减少“laplaceImg”在图像求和中的权重)? - Quentin Geissmann
我尝试了不同的内核,但都没有成功。那是标准图像,为什么我还需要更模糊它呢?也许我不应该使用cvAdd?如果这就是你的意思的话? - Mzk
我的意思是你的结果似乎有太多边缘。与其使用result = original+edge,不如使用result=a*original+(1-a)*edge,其中,在你的情况下,1>a>0.5。这有意义吗?你能展示一下你的“laplaceImg”吗?请注意“laplaceImg”的符号。 - Quentin Geissmann
嗨,Quentin,我已经上传了LaplaceImg。 - Mzk
4个回答

6
我认为问题在于你在对第二次导数之前模糊了图像。
这是使用C++ API的可行代码(我正在使用Opencv 2.4.3)。我也尝试过MATLAB,结果也是一样的。
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <iostream>

using namespace cv;
using namespace std;


int main(int /*argc*/, char** /*argv*/) {

    Mat img, imgLaplacian, imgResult;

    //------------------------------------------------------------------------------------------- test, first of all
    // now do it by hand
    img = (Mat_<uchar>(4,4) << 0,1,2,3,4,5,6,7,8,9,0,11,12,13,14,15); 

    // first, the good result
    Laplacian(img, imgLaplacian, CV_8UC1);
    cout << "let opencv do it" << endl;
    cout << imgLaplacian << endl;

    Mat kernel = (Mat_<float>(3,3) << 
        0,  1, 0,
        1, -4, 1,
        0,  1, 0); 
    int window_size = 3;

    // now, reaaallly by hand
    // note that, for avoiding padding, the result image will be smaller than the original one.
    Mat frame, frame32;
    Rect roi;
    imgLaplacian = Mat::zeros(img.size(), CV_32F);
    for(int y=0; y<img.rows-window_size/2-1; y++) {
        for(int x=0; x<img.cols-window_size/2-1; x++) {
            roi = Rect(x,y, window_size, window_size);
            frame = img(roi);
            frame.convertTo(frame, CV_32F);
            frame = frame.mul(kernel);
            float v = sum(frame)[0];
            imgLaplacian.at<float>(y,x) = v;
        }
    }
    imgLaplacian.convertTo(imgLaplacian, CV_8U);
    cout << "dudee" << imgLaplacian << endl;

    // a little bit less "by hand"..
    // using cv::filter2D
    filter2D(img, imgLaplacian, -1, kernel);
    cout << imgLaplacian << endl;


    //------------------------------------------------------------------------------------------- real stuffs now
    img = imread("moon.jpg", 0); // load grayscale image

    // ok, now try different kernel
    kernel = (Mat_<float>(3,3) << 
        1,  1, 1,
        1, -8, 1,
        1,  1, 1); // another approximation of second derivate, more stronger

    // do the laplacian filtering as it is
    // well, we need to convert everything in something more deeper then CV_8U
    // because the kernel has some negative values, 
    // and we can expect in general to have a Laplacian image with negative values
    // BUT a 8bits unsigned int (the one we are working with) can contain values from 0 to 255
    // so the possible negative number will be truncated
    filter2D(img, imgLaplacian, CV_32F, kernel);
    img.convertTo(img, CV_32F);
    imgResult = img - imgLaplacian;

    // convert back to 8bits gray scale
    imgResult.convertTo(imgResult, CV_8U);
    imgLaplacian.convertTo(imgLaplacian, CV_8U);

    namedWindow("laplacian", CV_WINDOW_AUTOSIZE);
    imshow( "laplacian", imgLaplacian );

    namedWindow("result", CV_WINDOW_AUTOSIZE);
    imshow( "result", imgResult );

    while( true ) {
        char c = (char)waitKey(10);
        if( c == 27 ) { break; }
    }

    return 0;
}

玩得开心!


1

我认为主要问题在于您使用了img + laplace,而img - laplace会得到更好的结果。我记得img - 2 * laplace效果最好,但我找不到我在哪里读到的,可能是我在大学读的书中。


1
你需要执行 img - laplace 而不是 img + laplace
laplace: f(x,y) = f(x-1,y+1) + f(x-1,y-1) + f(x,y+1) + f(x+1,y) - 4*f(x,y)

因此,如果您从原始图像中减去拉普拉斯,则会发现前面的4*f(x,y)的负号被否定,这个术语变为正数。

您还可以使用-5代替中心像素的-4来生成核,以使拉普拉斯成为一个一步过程,而不是获取拉普拉斯并执行img-laplace。为什么?请自行推导。

这将是最终的核。

Mat kernel = (Mat_(3,3) << -1, 0, -1, 0, -5, 0, -1, 0, -1);


也就是说,如果拉普拉斯滤波器的中心像素为负数,我们必须进行减法操作;如果它是正数,我们必须进行加法操作。 - hafiz031

0

在图像处理中,一个众所周知的结果是,如果从图像中减去其拉普拉斯算子,则会放大图像边缘,从而得到更清晰的图像。

拉普拉斯滤波器核算法:sharpened_pixel = 5 * current – left – right – up – down

输入图像描述

因此,代码将如下所示:

void sharpen(const Mat& img, Mat& result)
{    
    result.create(img.size(), img.type());
    //Processing the inner edge of the pixel point, the image of the outer edge of the pixel should be additional processing
    for (int row = 1; row < img.rows-1; row++)
    {
        //Front row pixel
        const uchar* previous = img.ptr<const uchar>(row-1);
        //Current line to be processed
        const uchar* current = img.ptr<const uchar>(row);
        //new row
        const uchar* next = img.ptr<const uchar>(row+1);
        uchar *output = result.ptr<uchar>(row);
        int ch = img.channels();
        int starts = ch;
        int ends = (img.cols - 1) * ch;
        for (int col = starts; col < ends; col++)
        {
            //The traversing pointer of the output image is synchronized with the current row, and each channel value of each pixel in each row is given a increment, because the channel number of the image is to be taken into account.
            *output++ = saturate_cast<uchar>(5 * current[col] - current[col-ch] - current[col+ch] - previous[col] - next[col]);
        }
    } //end loop
    //Processing boundary, the peripheral pixel is set to 0
    result.row(0).setTo(Scalar::all(0));
    result.row(result.rows-1).setTo(Scalar::all(0));
    result.col(0).setTo(Scalar::all(0));
    result.col(result.cols-1).setTo(Scalar::all(0));
}

int main()
{    
    Mat lena = imread("lena.jpg");
    Mat sharpenedLena;
    ggicci::sharpen(lena, sharpenedLena);

    imshow("lena", lena);
    imshow("sharpened lena", sharpenedLena);
    cvWaitKey();
    return 0;
}

如果你是一个懒人,那么接下来的内容会让你感到愉快。

 int main()
 {    
     Mat lena = imread("lena.jpg");
     Mat sharpenedLena;
     Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
     cv::filter2D(lena, sharpenedLena, lena.depth(), kernel);

     imshow("lena", lena);
     imshow("sharpened lena", sharpenedLena);
    cvWaitKey();
    return 0;
}

结果如下。输入图像描述


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接