二階微分算子-拉普拉斯算子


在前面博客結尾,我們簡要談了一下二階微分算子;對於圖像;

從上面可以看出

     一階微分算子,就是求圖像灰度變化曲線的導數,能夠突出圖像中的對象邊緣;
     二階微分算子,求圖像灰度變化導數的導數,對圖像中灰度變化強烈的地方很敏感,從而可以突出圖像的紋理結構
下面介紹幾個常見二階微分算子:


其模板為:


       圖(a)表示離散拉普拉斯算子的模板,圖(b)表示其擴展模板,圖(c)(d)則分別表示其他兩種拉普拉斯的實現模板。從模板形式容易看出,如果在圖像中一個較暗的區域中出現了一個亮點,那么用拉普拉斯運算就會使這個亮點變得更亮。因為圖像中的邊緣就是那些灰度發生跳變的區域,所以拉普拉斯銳化模板在邊緣檢測中很有用。一般增強技術對於陡峭的邊緣和緩慢變化的邊緣很難確定其邊緣線的位置。但此算子卻可用二次微分正峰和負峰之間的過零點來確定,對孤立點或端點更為敏感,因此特別適用於以突出圖像中的孤立點、孤立線或線端點為目的的場合。同梯度算子一樣,拉普拉斯算子也會增強圖像中的噪聲,有時用拉普拉斯算子進行邊緣檢測時,可將圖像先進行平滑處理。

       圖像銳化處理的作用是使灰度反差增強,從而使模糊圖像變得更加清晰。圖像模糊的實質就是圖像受到平均運算或積分運算,因此可以對圖像進行逆運算,如微分運算能夠突出圖像細節,使圖像變得更為清晰。由於拉普拉斯是一種微分算子,它的應用可增強圖像中灰度突變的區域,減弱灰度的緩慢變化區域。因此,銳化處理可選擇拉普拉斯算子對原圖像進行處理,產生描述灰度突變的圖像,再將拉普拉斯圖像與原始圖像疊加而產生銳化圖像。拉普拉斯銳化的基本方法可以由下式表示:

這種簡單的銳化方法既可以產生拉普拉斯銳化處理的效果,同時又能保留背景信息,將原始圖像疊加到拉普拉斯變換的處理結果中去,可以使圖像中的各灰度值得到保留,使灰度突變處的對比度得到增強,最終結果是在保留圖像背景的前提下,突現出圖像中小的細節信息。

下面實現簡單的圖像銳化,並調用函數實現邊緣檢測;

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

using namespace cv;
using namespace std;

//手動實現拉普拉斯算子圖像銳化  
void sharpenImage1(const Mat &image, Mat &result)
{
    result.create(image.size(), image.type());//為輸出圖像分配內容  
                                              /*拉普拉斯濾波核3*3
                                              0  -1   0
                                              -1   5  -1
                                              0  -1   0  */
                                              //處理除最外圍一圈外的所有像素值  
    for (int i = 1; i<image.rows - 1; i++)
    {
        const uchar * pre = image.ptr<const uchar>(i - 1);//前一行  
        const uchar * cur = image.ptr<const uchar>(i);//當前行,第i行  
        const uchar * next = image.ptr<const uchar>(i + 1);//下一行  
        uchar * output = result.ptr<uchar>(i);//輸出圖像的第i行  
        int ch = image.channels();//通道個數  
        int startCol = ch;//每一行的開始處理點  
        int endCol = (image.cols - 1)* ch;//每一行的處理結束點  
        for (int j = startCol; j < endCol; j++)
        {
//輸出圖像的遍歷指針與當前行的指針同步遞增, 以每行的每一個像素點的每一個通道值為一個遞增量, 因為要考慮到圖像的通道數
//saturate_cast<uchar>保證結果在uchar范圍內  
        *output++ = saturate_cast<uchar>(5 * cur[j] - pre[j] - next[j] - cur[j - ch] - cur[j + ch]);
        }
    }
    //將最外圍一圈的像素值設為0  
    result.row(0).setTo(Scalar(0));
    result.row(result.rows - 1).setTo(Scalar(0));
    result.col(0).setTo(Scalar(0));
    result.col(result.cols - 1).setTo(Scalar(0));
    /*/或者也可以嘗試將最外圍一圈設置為原圖的像素值
    image.row(0).copyTo(result.row(0));
    image.row(image.rows-1).copyTo(result.row(result.rows-1));
    image.col(0).copyTo(result.col(0));
    image.col(image.cols-1).copyTo(result.col(result.cols-1));*/
}

//調用OpenCV函數實現拉普拉斯算子圖像銳化  
void sharpenImage2(const Mat &image, Mat &result)
{
    Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
    filter2D(image, result, image.depth(), kernel);
}

//main函數  
void main() {
    Mat mat = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\01.jpg");
    Mat result1;
    Mat result2;
    Mat dst, gray;

    sharpenImage1(mat, result1);
    sharpenImage2(mat, result2);

    //濾波降噪
    GaussianBlur(mat, mat, Size(3, 3), 0, 0, BORDER_DEFAULT);

    //轉成灰度圖
    cvtColor(mat, gray, COLOR_BGR2GRAY);

    //運行Sobel算子,得到邊緣
    //求x方向梯度
    //Laplacian(src, dst, CV_16S, 3, 1,0,BORDER_DEFAULT);
    Laplacian(mat, dst, CV_16S);//后幾個參數有默認值

                                //由於是16位圖片,需要將圖片轉化成為8位圖形進行顯示 
    convertScaleAbs(dst, dst);
    imshow("調函數檢測邊緣", dst);

    namedWindow("src");
    namedWindow("手動實現");
    namedWindow("opencv實現");
    imshow("src", mat);
    imshow("手動實現", result1);
    imshow("opencv實現", result2);
    waitKey(0);
}

簡單介紹一下自帶函數:

 

使用拉普拉斯進行圖像銳化可參考博文:https://www.cnblogs.com/liu-jun/archive/2012/08/12/2635373.html

在前面,我們談到拉普萊斯變化有4個模板,我們一一實現,看看具體的情況

 

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

using namespace cv;
using namespace std;

//調用OpenV函數實現拉普拉斯算子圖像銳化 
/*拉普拉斯濾波核3*3
0   -1   0
-1   5  -1
0   -1   0  */
void LA(const Mat &image, Mat &result)
{
    Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 5, -1, 0, -1, 0);
    filter2D(image, result, image.depth(), kernel);
}

/*拉普拉斯濾波核3*3
0  -1   0
-1  4  -1
0  -1   0  */
void LB(const Mat &image, Mat &result)
{
    Mat kernel = (Mat_<float>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
    filter2D(image, result, image.depth(), kernel);
}
/*拉普拉斯濾波核3*3
0    1   0
1   -4   1
0    1   0  */
void LC(const Mat &image, Mat &result)
{
    Mat kernel = (Mat_<float>(3, 3) << 0, 1, 0, 1, -4, 1, 0, 1, 0);
    filter2D(image, result, image.depth(), kernel);
}


/*拉普拉斯濾波核3*3
1  1   1
1 -8   1
1  1   1  */
void LD(const Mat &image, Mat &result)
{
    Mat kernel = (Mat_<float>(3, 3) << 1, 1, 1, 1, -8, 1, 1, 1, 1);
    filter2D(image, result, image.depth(), kernel);
}

/*拉普拉斯濾波核3*3
-1  -1  -1
-1   8  -1
-1  -1  -1  */
void LE(const Mat &image, Mat &result)
{
    Mat kernel = (Mat_<float>(3, 3) << -1, -1, -1, -1, 8, -1, -1, -1, -1);
    filter2D(image, result, image.depth(), kernel);
}
//main函數  
void main() {
    Mat mat = imread("E:\\VS2015Opencv\\vs2015\\project\\picture\\01.jpg");
    Mat result1, A1, A2, A3;
    Mat result2;
    Mat dst, gray;


    //濾波降噪
    GaussianBlur(mat, mat, Size(3, 3), 0, 0, BORDER_DEFAULT);

     LA(mat, result1);
     LB(mat, A1);
     LC(mat, A2);
     LD(mat, A3);
     LE(mat, result2);

    //轉成灰度圖
    cvtColor(mat, gray, COLOR_BGR2GRAY);
    
    //運行Sobel算子,得到邊緣
    //求x方向梯度
    //Laplacian(src, dst, CV_16S, 3, 1,0,BORDER_DEFAULT);
    Laplacian(mat, dst, CV_16S);//后幾個參數有默認值

                                //由於是16位圖片,需要將圖片轉化成為8位圖形進行顯示 
    convertScaleAbs(dst, dst);
    imshow("調函數檢測邊緣", dst);
    
    namedWindow("src");
    imshow("src", mat);
    imshow("LA", result1);
    imshow("LB", A1);
    imshow("LC", A2);
    imshow("LC", A3);
    imshow("LD", result2);
    waitKey(0);
}

 

發現不同的模板,結果也不同。

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM