C++——bmp影象的平滑、銳化(拉普拉斯+sobel+prewitt+Roberts Cross operator)
本文實現了RGB三通道影象及灰度影象的平滑、銳化處理。在開始之前,我們先對平滑及銳化的概念進行一個簡單的瞭解:
1、平滑
影象平滑是指受感測器和大氣等因素的影響,遙感影象上會出現某些亮度變化過大的區域,或出現一些亮點(也稱噪聲)。這種為了抑制噪聲,使影象亮度趨於平緩的處理方法就是影象平滑。影象平滑實際上是低通濾波,平滑過程會導致影象邊緣模糊化。
常用的平滑處理方法有三種:
(1) Box模板去噪平滑處理,也就是均一化處理。
Box模板是{1,1,1,1,1,1,1,1,1}
(2) 高斯模板去噪平滑處理,就是在Box模板的基礎上加入了加權係數,考慮了距離某點位置越近影響越大的因素。相比Box模板,較為清晰一些。
高斯模板是{1,2,1,2,4,2,1,2,1}
(3) 中值濾波去噪平滑處理,就是將該點左右鄰近的兩個點的rgb值與該點自身進行比較,選擇其中最中間的值賦給該點。
2、銳化
影象銳化(image sharpening)是補償影象的輪廓,增強影象的邊緣及灰度跳變的部分,使影象變得清晰,分為空域處理和頻域處理兩類。影象銳化是為了突出影象上地物的邊緣、輪廓,或某些線性目標要素的特徵。這種濾波方法提高了地物邊緣與周圍像元之間的反差,因此也被稱為邊緣增強。
常用的銳化模板是拉普拉斯(Laplacian)運算元:
程式碼如下:
#include <cstring> #include <windows.h> #include"readbmp.h" #include"savebmp.h" int Template1[3][3] = { 1, 1, 1, 1, 1, 1, 1, 1, 1 };//平滑模板 均一化處理 int Template2[3][3] = { 1, 2, 1, 2, 4, 2, 1, 2, 1 };//高斯平模板 int Template3[3][3] = { 0, -1, 0, -1, 4, -1, 0, -1, 0 };//laplace銳化模板,4鄰域 int Template4[3][3] = { -1, -1, -1, -1, 8, -1, -1, -1, -1 };//laplace銳化模板,8鄰域 int sobelx[3][3] = { -1,-2,-1,0,0,0,1,2,1};//sobelx int sobely[3][3] = { -1,0,1,-2,0,2,-1,0,1};//sobel int coefficient1 = 9; int coefficient2 = 16; int coefficient3 = 1; void img_smooth_sharpen(int Template[][3], int coefficient) { char readPath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\11.bmp"; readBmp(readPath); unsigned char *imagedatasmooth; unsigned char *imagedata; imagedata = pBmpBuf; //平滑運算元也是通過模板進行處理的,所以可以把平滑處理和銳化處理通過一個函式實現 //分配新畫素素組的空間 int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4; imagedatasmooth = new unsigned char[lineByte * bmpHeight]; //進行模板操作 for (int i = 0; i < bmpHeight; i++) for (int j = 0; j < bmpWidth; j++) for (int k = 0; k < 3; k++) { if (i == 0 || j == 0 || i == bmpHeight - 1 || j == bmpWidth - 1) *(imagedatasmooth + i*bmpWidth*3 + j*3+k) = *(imagedata + i*bmpWidth*3 + j*3+k); else { int sum = 0; for (int m = i - 1; m < i + 2; m++) for (int n = j - 1; n < j + 2; n++) { sum += (*(imagedata + m*bmpWidth * 3 + n * 3 + k))*Template[n - j + 1][m - i + 1] / coefficient; }//8位BMP中一個畫素值,對應調色盤中索引號為該畫素值的項所存放的RGB色彩 //所以畫素值範圍為0~255,畫素值小於0就取0,大於255就取255 sum = (sum > 0) ? sum : 0; sum = (sum >255) ? 255 : sum; *(imagedatasmooth + i*bmpWidth * 3 + j * 3 + k) = sum; } } char writePath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\11s1.bmp"; saveBmp(writePath, imagedatasmooth, bmpWidth, bmpHeight, biBitCount, pColorTable); printf("平滑操作完成,請檢視bmp檔案。\n\n"); } void imggray_smooth_sharpen(int Template[][3], int coefficient) { char readPath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\11.bmp"; readBmp(readPath); unsigned char *imagedatasmooth; unsigned char *imagedata; imagedata = pBmpBuf; //平滑運算元也是通過模板進行處理的,所以可以把平滑處理和銳化處理通過一個函式實現 //分配新畫素素組的空間 int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4; imagedatasmooth = new unsigned char[lineByte * bmpHeight]; //進行模板操作 for (int i = 0; i < bmpHeight; i++) for (int j = 0; j < bmpWidth; j++) if (i == 0 || j == 0 || i == bmpHeight - 1 || j == bmpWidth - 1) *(imagedatasmooth + i*bmpWidth + j ) = *(imagedata + i*bmpWidth + j ); else { int sum = 0; for (int m = i - 1; m < i + 2; m++) for (int n = j - 1; n < j + 2; n++) { sum += (*(imagedata + m*bmpWidth + n ))*Template[n - j + 1][m - i + 1] / coefficient; }//8位BMP中一個畫素值,對應調色盤中索引號為該畫素值的項所存放的RGB色彩 //所以畫素值範圍為0~255,畫素值小於0就取0,大於255就取255 sum = (sum > 0) ? sum : 0; sum = (sum >255) ? 255 : sum; *(imagedatasmooth + i*bmpWidth + j ) = sum; } char writePath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\11s1.bmp"; saveBmp(writePath, imagedatasmooth, bmpWidth, bmpHeight, biBitCount, pColorTable); printf("平滑操作完成,請檢視bmp檔案。\n\n"); }
其中,img_smooth_sharpen( )對應的是RGB圖的平滑及銳化處理,imggray_smooth_sharpen( )對應的是灰度圖的平滑及銳化處理。
3、sobel運算元
索貝爾運算元(Sobel operator)主要用作邊緣檢測,在技術上,它是一離散性差分運算元,用來運算影象亮度函式的灰度之近似值。在影象的任何一點使用此運算元,將會產生對應的灰度向量或是其法向量。
Sobel卷積因子為:
該運算元包含兩組3x3的矩陣,分別為橫向及縱向,將之與影象作平面卷積,即可分別得出橫向及縱向的亮度差分近似值。如果以A代表原始影象,Gx及Gy分別代表經橫向及縱向邊緣檢測的影象灰度值,其公式如下:
影象的每一個畫素的橫向及縱向灰度值通過以下公式結合,來計算該點灰度的大小:
通常,為了提高效率 使用不開平方的近似值:
梯度計算方法如下:
Sobel運算元根據畫素點上下、左右鄰點灰度加權差,在邊緣處達到極值這一現象檢測邊緣。對噪聲具有平滑作用,提供較為精確的邊緣方向資訊,邊緣定位精度不夠高。當對精度要求不是很高時,是一種較為常用的邊緣檢測方法。
程式碼如下:
void imggray_sobel_sharpen(int Templatex[][3], int Templatey[][3], int coefficient)
{
char readPath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\gray.bmp";
readBmp(readPath);
unsigned char *imagedatasobel;
unsigned char *imagedatasobelx;
unsigned char *imagedatasobely;
unsigned char *imagedata;
imagedata = pBmpBuf;
//平滑運算元也是通過模板進行處理的,所以可以把平滑處理和銳化處理通過一個函式實現
//分配新畫素素組的空間
int lineByte = (bmpWidth * biBitCount / 8 + 3) / 4 * 4;
imagedatasobel = new unsigned char[lineByte * bmpHeight];
imagedatasobelx = new unsigned char[lineByte * bmpHeight];
imagedatasobely = new unsigned char[lineByte * bmpHeight];
//進行模板操作
for (int i = 0; i < bmpHeight; i++)
for (int j = 0; j < bmpWidth; j++)
if (i == 0 || j == 0 || i == bmpHeight - 1 || j == bmpWidth - 1)
{
*(imagedatasobelx + i*bmpWidth + j) = *(imagedata + i*bmpWidth + j);
*(imagedatasobely + i*bmpWidth + j) = *(imagedata + i*bmpWidth + j);
}
else
{
int sumx = 0;
int sumy = 0;
for (int m = i - 1; m < i + 2; m++)
for (int n = j - 1; n < j + 2; n++)
{
sumx += (*(imagedata + m*bmpWidth + n))*Templatex[n - j + 1][m - i + 1] / coefficient;
sumy += (*(imagedata + m*bmpWidth + n))*Templatey[n - j + 1][m - i + 1] / coefficient;
}//8位BMP中一個畫素值,對應調色盤中索引號為該畫素值的項所存放的RGB色彩
//所以畫素值範圍為0~255,畫素值小於0就取0,大於255就取255
sumx = (sumx > 0) ? sumx : 0;
sumx = (sumx >255) ? 255 : sumx;
*(imagedatasobelx + i*bmpWidth + j) = sumx;//sobelx
sumy = (sumy > 0) ? sumy : 0;
sumy = (sumy >255) ? 255 : sumy;
*(imagedatasobely + i*bmpWidth + j) = sumy;//sobely
*(imagedatasobel + i*bmpWidth + j) =abs( *(imagedatasobelx + i*bmpWidth + j)) + abs(*(imagedatasobely + i*bmpWidth + j));
}
char writePath[] = "D:\\C++_file\\image_deal_C++\\IMAGE_JIEQU\\11s1.bmp";
saveBmp(writePath, imagedatasobel, bmpWidth, bmpHeight, biBitCount, pColorTable);
printf("平滑操作完成,請檢視bmp檔案。\n\n");
}
4、普利維特運算元(Prewitt operate)
卷積因子如下:
計算 和sobel差不多;
Prewitt運算元利用畫素點上下、左右鄰點灰度差,在邊緣處達到極值檢測邊緣。對噪聲具有平滑作用,定位精度不夠高。
5、羅伯茨交叉邊緣檢測(Roberts Cross operator)
卷積因子如下:
灰度公式:
近似公式:
灰度方向計算公式為:
Roberts運算元採用對角線方向相鄰兩畫素之差近似梯度幅值檢測邊緣。檢測水平和垂直邊緣的效果好於斜向邊緣,定位精度高,對噪聲敏感。
這三種檢測運算元,本文僅用sobel進行了實驗,若要實驗其他運算元,需適當修改程式。