Laplace算子作为边缘检测之一,和Sobel算子一样也是工程数学中常用的一种积分变换,属于空间锐化滤波操作。拉普拉斯算子(Laplace Operator)是n维
欧几里德空间
中的一个二阶微分算子,定义为
梯度
(▽f)的
散度
(▽·f)。拉普拉斯算子也可以推广为定义在
黎曼流形
上的椭圆型算子,称为
拉普拉斯-贝尔特拉米算子
。(百度百科)
拉普拉斯算子是二阶微分线性算子,在图像边缘处理中,二阶微分的边缘定位能力更强,锐化效果更好,因此在进行
图像边缘处理时,直接采用二阶微分算子而不使用一阶微分。
图1
一阶微分和
二阶微分计算图
离散函数的导数退化成了差分,一维一阶差分公式和二阶差分公式分别为:如图2所示
图2
一阶微分和
二阶微分计算
分别对Laplace算子x,y两个方向的二阶导数进行差分就得到了离散函数的Laplace算子。在一个二维函数f(x,y)中,x,y两个方向的二阶差分分别为:如图3所示
图3 x,y两个方向的二阶差分
所以Laplace算子的差分形式为:
写成filter mask的形式如下:
该mask的特点,mask在上下左右四个90度的方向上结果相同,也就是说在90度方向上无方向性。为了让该mask在45度的方向上也具有该性质,对该filter mask进行扩展定义为
将Laplace算子写成filter mask后,其操作大同小异于其他的空间滤波操作。将filter mask在原图上逐行移动,然后mask中数值与其重合的像素相乘后求和,赋给与mask中心重合的像素,对图像的第一,和最后的行和列无法做上述操作的像素赋值零,就得到了拉普拉斯操作结果。因为Laplace算子是二阶导数操作,其在强调图像素中灰度不连续的部分的同时也不在强调灰度值连续的部分。这样会产生一个具有很明显的灰度边界,但是没有足够特征的黑色背景。背景特征可以通过原图像与Laplace算子操作后的图像混合恢复。用公式。
【C++代码】
// MyLaplace.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include <opencv2/opencv.hpp>
#include <opencv2/core.hpp>
using namespace std;
using namespace cv;
//**********************//
//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];
//*****************************//
//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));
int main()
Mat src = imread("D:/10.jpg");
//get some informations of original image
int nr = src.rows;
int nc = src.cols*3;
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
imshow("结果", LaResult_own);
waitKey(0);
View Code
【参考文献】
http://www.cnblogs.com/german-iris/p/4840647.html
https://baike.baidu.com/item/%E6%8B%89%E6%99%AE%E6%8B%89%E6%96%AF%E7%AE%97%E5%AD%90/7261323?fr=aladdin