OpenCV-跟我一起学数字图像处理之拉普拉斯算子

本文详细介绍了离散Laplace算子的数学原理及其在图像处理中的应用,包括一维和二维的差分公式、不同类型的滤波器掩膜及其实现方式,并通过OpenCV展示了具体的代码实现与效果对比。
部署运行你感兴趣的模型镜像
  • 数学原理

离散函数导数

离散函数的导数退化成了差分,一维一阶差分公式和二阶差分公式分别为,

CodeCogsEqn

CodeCogsEqn(2)

Laplace算子的差分形式

分别对Laplace算子x,y两个方向的二阶导数进行差分就得到了离散函数的Laplace算子。

在一个二维函数f(x,y)中,x,y两个方向的二阶差分分别为,

CodeCogsEqn(3)

CodeCogsEqn(4)

所以Laplace算子的差分形式为,

CodeCogsEqn(5)

写成filter mask的形式如下,

010
1-41
010
注意该mask的特点,mask在上下左右四个90度的方向上结果相同,也就是说在90度方向上无方向性。为了让该mask在45度的方向上也具有该性质,对该filter mask进行扩展定义为,
111
1-81
111
 

注:

有时我们也会见到不同于上述结果的Laplace算子的filter mask,

0-10
-14-1
0-10
 
-1-1-1
-18-1
-1-1-1

其原因是在定义二阶导数的时候采用了相反的定义,这个无关紧要,但是要注意,当用Laplace算子滤波后的图像与原图叠加时,混合操作是加还是减因上述的定义而异。

图像的Laplace操作

如同本文开始时说的那样,将Laplace算子写成filter mask后,其操作大同小异于其他的空间滤波操作。将filter mask在原图上逐行移动,然后mask中数值与其重合的像素相乘后求和,赋给与mask中心重合的像素,对图像的第一,和最后的行和列无法做上述操作的像素赋值零,就得到了拉普拉斯操作结果。

拉普拉斯操作结果与原图的混合

因为Laplace算子是二阶导数操作,其在强调图像素中灰度不连续的部分的同时也不在强调灰度值连续的部分。这样会产生一个具有很明显的灰度边界,但是没有足够特征的黑色背景。背景特征可以通过原图像与Laplace算子操作后的图像混合恢复。用公式,

CodeCogsEqn(6)

其中的参数c的取值和上面的两种mask定义有关,当mask中心的数值取正时c=-1,相反c=1;

  • 基于OpenCV的Laplace算子的计算

OpenCV中Laplacian函数可以实现对图像的Laplace操作,具体用法如下,

Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );

参数意义为,

  1. src_gray,输入图像
  2. dst,Laplace操作结果
  3. ddepth,输出图像深度,因为输入图像一般为CV_8U,为了避免数据溢出,输出图像深度应该设置为CV_16S
  4. kernel_size,filter mask的规模,我们的mask时3x3的,所以这里应该设置为3
  5. scale,delta,BORDER_DEFAULT,默认设置就好

基于OpenCV的Laplace算子仿真代码段如下,

复制代码
//load the Original Image and get some informations
Mat src = imread("012.jpg",0);
namedWindow("OriginalImage");
imshow("OriginalImage",src);
CV_Assert(src.depth() == CV_8U);

//OpenCV solution - Laplacian
Mat dst,abs_dst_laplace;
Laplacian(src,dst,CV_16S,3);
convertScaleAbs(dst,abs_dst_laplace);

//show the result
namedWindow("result_laplacian");
imshow("result_laplacian",abs_dst_laplace);
复制代码

其中convertScaleAbs函数功能是将CV_16S型的输出图像转变成CV_8U型的图像。

仿真结果:

原图:

original

Laplace操作结果:

abs_dst_laplae

  • 基于mask operation原理仿真

Laplace算子滤波仿真

根据数学原理中介绍的算法,编写相应代码,进行相关仿真。其中对Laplace操作结果进行了图像拉伸显示,因为Laplace操作结果的像素值范围可能落在了[0,255]之外,而计算机在显示的时候将赋值全部置为0,大于255的像素全部显示成255。

代码段如下,

复制代码
//get some informations of original image
int nr = src.rows;
int nc = src.cols;
int n = nr*nc;
int arr[9] = {0};

//scan the whole pixels of original image 
//and do Laplacian Operation
int* table_lap = new int[n];
int* table_orig = new int[n];
int l;
for (int i=0;i<n;i++)
{
    table_lap[i] = 0;
    table_orig[i] = 0;
}
for (int i=1;i<nr-1;i++)
{
    const uchar* previous = src.ptr<uchar>(i-1);
    const uchar* current = src.ptr<uchar>(i);
    const uchar* next = src.ptr<uchar>(i+1);
    for (int j=1;j<nc-1;j++)
    {
        for (int k=0;k<3;k++)
        {
            arr[k] = previous[j+k-1];
            arr[k+3] = current[j+k-1];
            arr[k+6] = next[j+k-1];
        }
        l = nc*i+j;        //calculate the location in the table of current pixel
        Lmaskoperation(table_lap,arr,l);
        table_orig[l] = arr[4];
    }
}

//pixels scale
uchar* La_scaled = new uchar[n];
table_scale(table_lap,La_scaled,n);

//padding values
Mat LaResult_own;
LaResult_own.create(src.size(),src.type());
uchar* p = NULL;
for (int i=0;i<nr;i++)
{
    p = LaResult_own.ptr<uchar>(i);
    for (int j=0;j<nc;j++)
    {
        l = nc*i+j;
        p[j] = La_scaled[l];
    }
}

//show results
namedWindow("LaResult_own");
imshow("LaResult_own",LaResult_own);
复制代码

其中Lmaskoperation是我写的mask为Laplace mask的mask operation操作函数,函数段如下,

复制代码
//**********************//
//Laplacian mask operation
//**********************//
void Lmaskoperation(int* table,int* arr,int l)
{
    int tmp[9] = {-1,-1,-1,-1,8,-1,-1,-1,-1};
    for (int i=0;i<9;i++)
    {
        table[l] = table[l] + tmp[i]*arr[i];
    }
}
复制代码

tabel_scale函数就是我写的图像拉伸函数,将Laplace操作结果拉伸到[0,255],具体函数段如下,

复制代码
//*****************************//
//scale the pixels to [0 255]
//*****************************//
void table_scale(int* table,uchar* result,int n)
{
    int min = table[0];
    int max = table[0];
    for (int i=0;i<n;i++)
    {
        if(min>table[i])
        {
            min = table[i];
        }
        if(max<table[i])
        {
            max = table[i];
        }
    }
    for (int i=0;i<n;i++)
    {
        result[i] = (uchar)(255*(table[i]-min)/(max-min));
    }
}
复制代码

仿真结果,拉伸后Laplace算子的操作结果

LaResult_own

以灰色为主色调的显示结果就是Laplace算子操作拉伸后显示的一大特点。

Laplace滤波图像与原图像的混合

我使用的mask中心值为正,所以混合操作需要原图减去Laplace滤波图像,代码段如下,

复制代码
//blending with the original image using Eq g(x,y)=f(x,y)+c*Lap(x,y)
int* table_blend = new int[n];
for(int i=0;i<n;i++)
{
    table_blend[i] = table_orig[i] - table_lap[i];
    if(table_blend[i]<0)
    {
        table_blend[i] = 0;
    }
    else if (table_blend[i]>255)
    {
        table_blend[i] = 255;
    }
}

//padding values to blending result
Mat Blresult;
Blresult.create(src.size(),src.type());
for (int i=0;i<nr;i++)
{
    p = Blresult.ptr<uchar>(i);
    for(int j=0;j<nc;j++)
    {
        l = nc*i+j;
        p[j] = table_blend[l];
    }
}

//show blending result
namedWindow("blending result_laplacian");
imshow("blending result_laplacian",Blresult);
复制代码

仿真结果:

blending result_laplacian

最后给出冈萨雷斯在介绍Laplacian时所给素材的仿真结果

blending result_laplacian

您可能感兴趣的与本文相关的镜像

ACE-Step

ACE-Step

音乐合成
ACE-Step

ACE-Step是由中国团队阶跃星辰(StepFun)与ACE Studio联手打造的开源音乐生成模型。 它拥有3.5B参数量,支持快速高质量生成、强可控性和易于拓展的特点。 最厉害的是,它可以生成多种语言的歌曲,包括但不限于中文、英文、日文等19种语言

<think>嗯,用户想用C++和OpenCV实现拉普拉斯算子进行图像处理,我得先理清楚拉普拉斯算子的基本概念和在OpenCV中的应用方法。根据引用中的资料,拉普拉斯算子主要用于图像增强和边缘检测,通过二阶微分来突出图像中的细节。 首先,用户可能需要了解如何在OpenCV中读取图像,转换成灰度图,因为很多处理都是基于灰度图像的。引用[1]和[2]都提到了使用imread函数读取图像,并转换为灰度模式,这可能是一个必要的步骤。接着,应用拉普拉斯算子的卷积核,引用[1]和[4]展示了不同的核,比如3x3的核,或者直接使用Laplacian函数。 然后,用户可能需要具体的代码示例。引用[1]提供了使用filter2D函数进行卷积操作的例子,而引用[4]则用了cv::Laplacian函数,参数中ksize的不同值可能有不同的效果。需要注意的是,ksize=1和3可能有相同的效果,这点在引用[4]中提到过,可能需要进一步解释。 另外,图像处理后的归一化和显示也是关键步骤。引用[1]中的normalize函数和convertScaleAbs确保结果图像正确显示,而引用[3]提到了输出图像的转换和峰值信噪比的计算,这可能对高级用户有用。 用户可能还想了解不同实现方法的区别,比如手动定义核和使用内置函数的优缺点。引用[5]解释了拉普拉斯算子的基本思想,强调其用于图像锐化,这可以作为背景知识补充。 最后,常见问题和优化方法也是用户关心的。比如如何选择卷积核,处理后的图像如何与原始图像叠加,以及性能优化方法如高斯模糊预处理(引用[4])和频率域优化(引用[3])。需要确保代码示例正确,并解释每个步骤的作用,比如为什么要进行归一化,不同的ksize参数的影响等。 可能还需要提醒用户注意图像边界处理,引用[3]中的copyMakeBorder函数可能与此相关。另外,错误处理,如图像读取失败的情况,如引用[2]中的检查image.empty(),也是代码健壮性的体现。 总结来说,回答需要分步骤说明实现方法,提供两种代码示例,解释关键步骤,并解答可能的相关问题,帮助用户全面理解和应用拉普拉斯算子OpenCV中的实现。</think>以下是如何使用C++和OpenCV实现拉普拉斯算子进行图像处理的详细步骤及代码示例: --- ### 一、拉普拉斯算子实现方法 拉普拉斯算子是一种基于二阶导数的边缘检测算子,可用于图像增强和边缘提取。在OpenCV中主要通过两种方式实现: 1. **手动定义卷积核**:使用$3 \times 3$的拉普拉斯核进行卷积操作[^1][^5] 2. **调用内置函数**:直接使用`cv::Laplacian()`函数[^4] --- ### 二、代码实现步骤 #### 方法1:手动定义卷积核 ```cpp #include <opencv2/opencv.hpp> using namespace cv; int main() { // 读取图像并转为灰度图 Mat image = imread("input.jpg", IMREAD_GRAYSCALE); if(image.empty()) return -1; // 错误处理[^2] // 定义拉普拉斯核(中心为-4) Mat kernel = (Mat_<float>(3,3) << 0, 1, 0, 1, -4, 1, 0, 1, 0); // 经典四邻域核[^1] // 卷积操作 Mat result; filter2D(image, result, CV_32F, kernel); // 使用浮点型输出 // 归一化并转换类型 normalize(result, result, 0, 255, NORM_MINMAX); convertScaleAbs(result, result); // 转换为8位无符号整型[^3] imshow("Laplacian Result", result); waitKey(0); return 0; } ``` #### 方法2:使用OpenCV内置函数 ```cpp #include <opencv2/opencv.hpp> using namespace cv; int main() { Mat src = imread("input.jpg", IMREAD_GRAYSCALE); GaussianBlur(src, src, Size(3,3), 0); // 预处理降噪[^4] Mat dst; Laplacian(src, dst, CV_16S, 3); // ksize=3对应3x3核 convertScaleAbs(dst, dst); // 绝对值转换 imshow("Built-in Laplacian", dst); waitKey(0); return 0; } ``` --- ### 三、关键参数说明 1. **卷积核选择**: - 四邻域核:中心值为-4(如上述代码) - 八邻域核:中心值为-8(修改核的四个对角为1) 2. **数据类型转换**: - 卷积结果需用`CV_32F`或`CV_16S`类型存储负值 - `convertScaleAbs()`将结果映射到0-255范围 3. **预处理优化**: - 高斯模糊可减少噪声干扰(推荐3x3或5x5核)[^4] - 频率域实现可优化计算效率(见引用[3]的DFT方法) --- ### 四、效果对比与优化 | 方法 | 优点 | 缺点 | |------|------|------| | 手动卷积 | 核定义灵活 | 需处理边界问题 | | 内置函数 | 自动边界填充 | 仅支持特定核尺寸 | 优化建议: 1. 结合图像叠加增强边缘:`output = original - laplacian_result` 2. 使用频率域方法加速大规模图像处理 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值