1. 拉普拉斯算子
Laplace算子是一種各向同性算子,二階微分算子,在只關心邊緣的位置而不考慮其周圍的象素灰度差值時比較合適。Laplace算子對孤立象素的響應要比對邊緣或線的響應要更強烈,因此只適用於無噪聲圖象。存在噪聲情況下,使用Laplacian算子檢測邊緣之前需要先進行低通濾波。所以,通常的分割算法都是把Laplacian算子和平滑算子結合起來生成一個新的模板。
拉普拉斯算子,二階微分線性算子。與一階微分相比,二階微分的邊緣定位能力更強,銳化效果更好。使用二階微分算子的基本方法是定義一種二階微分的離散形式,然後根據這個形式生成一個濾波模板,與圖像卷積。
各向同性濾波器,圖像旋轉後響應不變,這就要求濾波模板自身是對稱的,如果不對稱,結果就是,當原圖旋轉90°時,原圖某一點能檢測出細節(突變)的,現在卻檢測不出來,這就是各向異性的原因。我們更關心的是各向同性濾波模板,對圖像的旋轉不敏感。
對於二維圖像 f(x,y),二階微分最簡單的定義–拉普拉斯算子定義爲:
對於任意階微分算子都是線性算子,所以二階微分算子和後面的一階微分算子都可以用生成模板然後卷積的方式得出結果。
也就是一個點的拉普拉斯的算子計算結果是上下左右的灰度的和減去本身灰度的四倍。同樣,可以根據二階微分的不同定義,所有符號相反,也就是上式所有灰度值全加上負號,就是-1,-1,-1,-1,4。但要注意,符號改變,銳化的時候與原圖的加或減應當相對變化。上面是四鄰接的拉普拉斯算子,將這個算子旋轉45°後與原算子相加,就變成八鄰域的算子了,也就是一個像素周圍一圈8個像素的和與中間像素8倍的差,作爲拉普拉斯計算結果。
因爲要強調圖像中突變(細節),所以平滑灰度的區域,無響應,即模板係數的和爲0,也是二階微分必備條件。
最後的銳化公式:
g是輸出,f爲原始圖像,c是係數,也就是要加上多少細節的多少,與上一篇的銳化過程是一致的,先提取細節,然後再加(或者減去負細節)到原圖中。
得到濾波模板,此模板尺寸恆定,就是3x3,不像平滑模板,尺寸可變:
拉式算子用來改善因擴散效應的模糊特別有效,因爲它符合降制模型。擴散效應是成像過程中經常發生的現象。
Laplacian算子一般不以其原始形式用於邊緣檢測,因爲其作爲一個二階導數,Laplacian算子對噪聲具有無法接受的敏感性;同時其幅值產生算邊緣,這是複雜的分割不希望有的結果;最後Laplacian算子不能檢測邊緣的方向;所以Laplacian在分割中所起的作用包括:
(1)利用它的零交叉性質進行邊緣定位;
(2)確定一個像素是在一條邊緣暗的一面還是亮的一面;
一般使用的是高斯型拉普拉斯算子(Laplacian of a
Gaussian,LoG),由於二階導數是線性運算,利用LoG卷積一幅圖像與首先使用高斯型平滑函數卷積改圖像,然後計算所得結果的拉普拉斯是一樣的。所以在LoG公式中使用高斯函數的目的就是對圖像進行平滑處理,使用Laplacian算子的目的是提供一幅用零交叉確定邊緣位置的圖像;圖像的平滑處理減少了噪聲的影響並且它的主要作用還是抵消由Laplacian算子的二階導數引起的逐漸增加的噪聲影響。
2. 月球圖像
本圖像大小爲400*400,8位灰度圖像。
3. 代碼實現
平臺:vs2015
語言:C語言
流程:
1)讀取圖片
2)對每個像素進行拉普拉斯計算
3)輸出處理後的圖片
4)改變拉普拉斯算子,重複上述流程
關鍵代碼:
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
ia1 = (i + 1 > width - 1) ? width - 1 : i + 1;
ja1 = (j + 1 > width - 1) ? width - 1 : j + 1;
im1 = (i - 1 < 0) ? 0 : i - 1;
jm1 = (j - 1 < 0) ? 0 : j - 1;
temp = input[im1 * width + j] + input[i * width + jm1] - 4 *
input[i * width + j] + input[i * width + ja1] + input[ia1 * width + j];
temp = temp + input[i * width + j];
if (temp < 0)temp = 0;
output[i * width + j] = (unsigned char)temp;
}
}
處理結果:
分析:
觀察圖4可以看到月球表面的邊緣部分、變化的部分更加突出和明顯,起到了銳化的作用
圖3顯然不太對,分析後感覺是不能用
0 1 0
1 -3 1
0 1 0
改爲
0 1 0
1 -4 1
0 1 0
計算後temp = -temp + input[i * width + j];
結果爲
考慮到圖1和圖2不一樣,所以圖5和圖3儘管相似其實不一樣。
4. 遇到問題
以前寫的代碼有一處bug
//保存文件
fp = fopen("output.raw", "wb");
for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
fwrite(output, 1, 1, fp);
output++;
}
}
fclose(fp);
這一次添加了釋放free(output);
結果報錯,最終原因是此時output的位置已經變了,需要添加一句
output -= height * width;
5. 附代碼:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#define height 400
#define width 400
typedef unsigned char BYTE; // 定義BYTE類型,佔1個字節
int main(void)
{
FILE *fp = NULL;
BYTE *input = NULL;
BYTE *output = NULL;
int temp;
int ia1,ja1,im1,jm1;
input = (BYTE*)malloc(width * height * sizeof(BYTE));
output = (BYTE*)malloc(width * height * sizeof(BYTE));
fp = fopen("blurry_moon.raw", "rb");
for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
fread(input, 1, 1, fp);
input++;
}
}
input -= height * width;
fclose(fp);
//拉普拉斯
// 0 +1 0
// +1 -4 +1
// 0 +1 0
for (int i = 0; i < height; i++)
{
for (int j = 0; j < width; j++)
{
ia1 = (i + 1 > width - 1) ? width - 1 : i + 1;
ja1 = (j + 1 > width - 1) ? width - 1 : j + 1;
im1 = (i - 1 < 0) ? 0 : i - 1;
jm1 = (j - 1 < 0) ? 0 : j - 1;
temp = input[im1 * width + j] + input[i * width + jm1] - 4 *
input[i * width + j] + input[i * width + ja1] + input[ia1 * width + j];
temp = temp + input[i * width + j];
if (temp < 0)temp = 0;
output[i * width + j] = (unsigned char)temp;
}
}
//保存文件
fp = fopen("output.raw", "wb");
for (int i = 0; i < height; i++){
for (int j = 0; j < width; j++){
fwrite(output, 1, 1, fp);
output++;
}
}
output -= height * width;
fclose(fp);
free(input);
free(output);
return 0;
}