在前兩部文章介紹了幾種邊緣檢測演算法,和點陣圖的記憶體結構。如果對前兩篇文章已經理解透徹
了,那麼本文將帶你進入數字影象處理的世界。
本文通過C程式碼實現基本的sobel邊緣檢測,包括8個方向和垂直方向:
程式碼參考之前一篇--一個例項弄清楚點陣圖的儲存結構。
同樣,程式碼中附有詳細解釋,於是我不再對程式碼作過多講解:
標頭檔案TestBmp.h如下:
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef long LONG; //點陣圖檔案頭定義;
//其中不包含檔案型別資訊(由於結構體的記憶體結構決定,
//要是加了的話將不能正確讀取檔案資訊)
typedef struct tagBITMAPFILEHEADER{
//WORD bfType;//檔案型別,必須是0x424D,即字元“BM”
DWORD bfSize;//檔案大小
WORD bfReserved1;//保留字
WORD bfReserved2;//保留字
DWORD bfOffBits;//從檔案頭到實際點陣圖資料的偏移位元組數
}BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//資訊頭大小
LONG biWidth;//影象寬度
LONG biHeight;//影象高度
WORD biPlanes;//位平面數,必須為1
WORD biBitCount;//每畫素位數
DWORD biCompression; //壓縮型別
DWORD biSizeImage; //壓縮影象大小位元組數
LONG biXPelsPerMeter; //水平解析度
LONG biYPelsPerMeter; //垂直解析度
DWORD biClrUsed; //點陣圖實際用到的色彩數
DWORD biClrImportant; //本點陣圖中重要的色彩數
}BITMAPINFOHEADER; //點陣圖資訊頭定義 typedef struct tagRGBQUAD{
BYTE rgbBlue; //該顏色的藍色分量
BYTE rgbGreen; //該顏色的綠色分量
BYTE rgbRed; //該顏色的紅色分量
BYTE rgbReserved; //保留值
}RGBQUAD;//調色盤定義
原始檔TestBmp.cpp如下:
#include<math.h>
#include <iomanip.h>
#include <stdlib.h>
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <fstream.h> /************************************************************************/ /*以下該模組是完成BMP影象(彩色影象是24bit RGB各8bit)的畫素獲取, 並存在檔名為xiang_su_zhi.txt中
*/
/************************************************************************/ //用到的全域性變數 unsigned char *pBmpBuf;//讀入影象資料的指標 int bmpWidth;//影象的寬 int bmpHeight;//影象的高 RGBQUAD *pColorTable;//顏色表指標 int biBitCount;//影象型別,每畫素位數 /*static s[8][9]={
{-1,-2,-1,0,0,0,1,2,1},
{0,-1,-2,1,0,-1,2,1,0},
{1,0,-1,2,0,-2,1,0,-1},
{2,1,0,1,0,-1,0,-1,-2},
{1,2,1,0,0,0,-1,-2,-1},
{0,1,2,-1,0,1,-2,-1,0},
{-1,0,1,-2,0,2,-1,0,1},
{-2,-1,0,-1,0,1,0,1,2}
};*/ static s[]={-,,,-,,,-,,}; /************************************************************************/ /* 讀影象的點陣圖資料、寬、高、顏色表及 每畫素位數等資料進記憶體,存放在相應的全域性變數中
*/
/************************************************************************/ bool readBmp(char *bmpName)
{ FILE *fp=fopen(bmpName,"rb");//二進位制只讀方式開啟指定的影象檔案 if(fp==) //判斷檔案是否正確開啟
return ; //跳過點陣圖檔案頭結構BITMAPFILEHEADER,使得檔案指標指向資訊頭的開始
fseek(fp, sizeof(BITMAPFILEHEADER),); //定義點陣圖資訊頭結構變數,讀取點陣圖資訊頭進記憶體,存放在變數head中,為了獲取影象的寬,高,每畫素所佔位數
BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), ,fp); //獲取影象寬、高、每畫素所佔位數等資訊
bmpWidth = head.biWidth;
bmpHeight = head.biHeight;
biBitCount = head.biBitCount; //定義變數,計算影象每行畫素所佔的位元組數(必須是4的倍數)
int lineByte=(bmpWidth * biBitCount/+)/*; //灰度影象有顏色表(調色盤),且顏色表表項為256
if(biBitCount==)
{
//申請顏色表所需要的空間,讀顏色表進記憶體
pColorTable=new RGBQUAD[]; //申請256種顏色表大小的記憶體
fread(pColorTable,sizeof(RGBQUAD),,fp); //讀取概灰度圖的顏色表到pColorTable所指向的記憶體中
} //申請點陣圖資料所需要的空間,讀點陣圖資料進pBmpBuf指向的記憶體
pBmpBuf=new unsigned char[lineByte * bmpHeight];
fread(pBmpBuf,,lineByte * bmpHeight,fp); fclose(fp);//關閉檔案
return ;//讀取檔案成功 } /************************************************************************/ /* 給定一個影象點陣圖資料、寬、高、顏色表指標及 每畫素所佔的位數等資訊,將其寫到指定檔案中
*/
/************************************************************************/ bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height, int biBitCount, RGBQUAD *pColorTable)
{
//如果點陣圖資料指標為0,則沒有資料傳入,函式返回
if(!imgBuf)
return ; //顏色表大小,以位元組為單位,灰度影象顏色表為1024位元組,彩色影象顏色表大小為0
int colorTablesize=; if(biBitCount==)
colorTablesize=;//一個RGBQUAD(顏色)結構佔四個位元組,對於8位的灰度圖而言,一共有256*4=1024個位元組大小的顏色表 //待儲存影象資料每行位元組數為4的倍數
int lineByte=(width * biBitCount/+)/*; //以二進位制寫的方式開啟檔案
FILE *fp=fopen(bmpName,"wb");
if(fp==)
return ; //申請點陣圖檔案頭結構變數,填寫檔案頭資訊
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;//bmp型別 //bfSize是影象檔案4個組成部分之和
fileHead.bfSize= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + colorTablesize + lineByte*height;
fileHead.bfReserved1 = ;
fileHead.bfReserved2 = ; //bfOffBits是影象檔案前3個部分所需空間之和
fileHead.bfOffBits=+colorTablesize; //寫檔案頭進檔案
fwrite(&fileHead, sizeof(BITMAPFILEHEADER),, fp); //申請點陣圖資訊頭結構變數,填寫資訊頭資訊
BITMAPINFOHEADER head;
head.biBitCount=biBitCount;
head.biClrImportant=;
head.biClrUsed=;
head.biCompression=;
head.biHeight=height;
head.biPlanes=;
head.biSize=;
head.biSizeImage=lineByte*height;
head.biWidth=width;
head.biXPelsPerMeter=;
head.biYPelsPerMeter=; //寫點陣圖資訊頭進記憶體
fwrite(&head, sizeof(BITMAPINFOHEADER),, fp); //如果灰度影象,有顏色表,寫入檔案
if(biBitCount==)
fwrite(pColorTable, sizeof(RGBQUAD),, fp); //寫點陣圖資料進檔案
fwrite(imgBuf, height*lineByte, , fp); //關閉檔案
fclose(fp);
return ;
} void sobel()
{
//讀入指定BMP檔案進記憶體
char readPath[]="line.bmp"; //讀取影象資訊
readBmp(readPath); //輸出影象的資訊 寬度(單位畫素) 高度(單位畫素) 每個畫素的位數
cout<<"width="<<bmpWidth<<" height="<<bmpHeight<<" biBitCount="<<biBitCount<<endl; //每行位元組數(影象資料真正的寬度)
int RealWidth=(bmpWidth*biBitCount/+)/*; BYTE *pBmpSobel=new BYTE[RealWidth*bmpHeight];
memset(pBmpSobel,,RealWidth*bmpHeight); //////////////////////////////////////////////////////////////////////////
//八個方向的Sobel邊緣檢測
////////////////////////////////////////////////////////////////////////// /* int d,max; for(int y=1;y<bmpHeight-1;y++)
{
for(int x=1;x<RealWidth-1;x++)
{
max=0;
//////////////////////////////////////////////////////////////////////////
//分別從八個方向進行Sobel邊緣化,上 下 左 右,左上,左下,右上,右下
//////////////////////////////////////////////////////////////////////////
for(int i=0;i<8;i++)
{
d=0;
//////////////////////////////////////////////////////////////////////////
//一個sobel運算元和一個3*3的畫素矩陣相乘得到一個邊緣影象畫素值
//////////////////////////////////////////////////////////////////////////
for(int y2=0;y2<3;y2++)
for(int x2=0;x2<3;x2++)
{
d+=s[i][x2+y2*3]*pBmpBuf[(y-1+y2)*RealWidth+x-1+x2];
}
////////////////////////////////////////////////////////////////////////// if(d>max)max=d; //取出八個方向上的最大值儲存在max中,作為灰度圖某畫素點的邊緣畫素值
}
if(max>255)max=255; //將邊緣畫素值大於255的畫素點 畫素值置為255(白色)
pBmpSobel[y*RealWidth+x]=(BYTE)max;
}
}*/ //////////////////////////////////////////////////////////////////////////
//一個方向的Sobel邊緣檢測(垂直方向)
////////////////////////////////////////////////////////////////////////// int d; for(int y=;y<bmpHeight-;y++)
{
for(int x=;x<RealWidth-;x++)
{
d=;
//////////////////////////////////////////////////////////////////////////
//一個sobel運算元和一個3*3的畫素矩陣相乘得到一個邊緣影象畫素值
//////////////////////////////////////////////////////////////////////////
for(int y2=;y2<;y2++)
for(int x2=;x2<;x2++)
{
d+=s[x2+y2*]*pBmpBuf[(y-+y2)*RealWidth+x-+x2];
}
////////////////////////////////////////////////////////////////////////// if(d>)d=; //將邊緣畫素值大於255的畫素點 畫素值置為255(白色)
if(d<)d=; //將邊緣畫素值大於255的畫素點 畫素值置為255(黑色)
pBmpSobel[y*RealWidth+x]=(BYTE)d;
}
} //將影象資料存檔 char writePath[]="f.bmp"; //圖片處理後再儲存
saveBmp(writePath, pBmpSobel, bmpWidth, bmpHeight, biBitCount, pColorTable); //清除緩衝區,pBmpBuf和pColorTable是全域性變數,在檔案讀入時申請的空間
delete []pBmpBuf;
delete []pBmpSobel; if(biBitCount==)
delete []pColorTable;
} /************************************************************************/ /* 主函式 */ /************************************************************************/ void main()
{
sobel();
}
灰度原圖line.bmp(垂直方向上):
垂直方向的sobel圖為f.bmp:
八個方向的灰度原圖test.bmp:
八個方向的sobel邊緣結果圖f.bmp:
對該灰度原圖的垂直sobel: