1. 程式人生 > >C++——bmp影象的平滑、銳化(拉普拉斯+sobel+prewitt+Roberts Cross operator)

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進行了實驗,若要實驗其他運算元,需適當修改程式。