C++——bmp二值影象的腐蝕、膨脹、開運算、閉運算
本文實現二值bmp影象的腐蝕、膨脹及開運算、閉運算操作。本文使用白色為前景、黑色為背景的二值圖進行操作:
1、腐蝕
腐蝕操作是結構原中心在被腐蝕影象中平移填充的過程。影象A被結構元B腐蝕,用集合論來表示如下式:
其中x是平移量,上式表示腐蝕結果集合中的元素是結構元的中心平移x後仍然包含在集合A內,還可用E(A,B)表示腐蝕。
作影象腐蝕處理時,如果結構元素中的所有白色點與它對應的大影象素點完全相同,該點為白色,否則為黑色。
#include <cstring> #include <windows.h> #include"readbmp.h" #include"savebmp.h" //--------------------腐蝕操作-------------------------------------------// void img_erosion(int *TempBuf, int TempH, int TempW) { char readPath[] = "D:\\C++_file\\image_deal_C++\\image_morphology\\photo\\lunpan.bmp"; readBmp(readPath); unsigned char *pGrayData; //因為轉換後多了個顏色表,所以要改變 bfoffbits += (sizeof(RGBQUAD) * 256); //biSizeImg儲存的為點陣圖資料佔用的位元組數,轉換為灰度影象後值發生改變, //因為24為真彩色點陣圖資料的一個畫素用3各位元組表示,灰度影象為1個位元組 biBitCount = 8; int lineBytes = (bmpWidth * biBitCount / 8 + 3) / 4 * 4; int oldLineBytes = (bmpWidth * 24 / 8 + 3) / 4 * 4;; pGrayData = new unsigned char[lineBytes * bmpHeight]; //memset(pGrayData, 0, bmpInfoHeader.biSizeImage); //-------------------------------定義灰度影象的顏色表--------------------// pColorTable = new RGBQUAD[256]; for (int i = 0; i < 256; i++) { (*(pColorTable + i)).rgbBlue = i; (*(pColorTable + i)).rgbGreen = i; (*(pColorTable + i)).rgbRed = i; (*(pColorTable + i)).rgbReserved = 0; } //--------------------------------將RGB轉換為灰度值------------------------// int red, green, blue; BYTE gray; //char gray_1; int threshold = 128; for (int i = 0; i < bmpHeight; i++) { for (int j = 0; j < bmpWidth; j++) { red = *(pBmpBuf + i*oldLineBytes + 3 * j + 2); green = *(pBmpBuf + i*oldLineBytes + 3 * j + 1); blue = *(pBmpBuf + i*oldLineBytes + 3 * j); gray = (BYTE)((77 * red + 151 * green + 28 * blue) >> 8); //gray_1 = red*0.299 + green*0.587 + blue*0.114; *(pGrayData + i*lineBytes + j) = gray; if (*(pGrayData + i*lineBytes + j)>threshold) *(pGrayData + i*lineBytes + j) = 0; else *(pGrayData + i*lineBytes + j) = 255; } } unsigned char *p_erosion; p_erosion = new unsigned char[lineBytes * bmpHeight]; memcpy(p_erosion, pGrayData, lineBytes*bmpHeight); int flag; for (int i = TempH / 2; i<bmpHeight - TempH / 2; i++) { for (int j = TempW / 2; j<bmpWidth - TempW / 2; j++) { flag = 1; for (int k = -TempH / 2; k <= TempH / 2; k++) { for (int l = -TempW / 2; l <= TempW / 2; l++) { if (TempBuf[(k + TempH / 2)*TempW + l + TempW / 2]) { if (!*(pGrayData + (i + k)*lineBytes + j + l)) flag = 0; } } } if (flag) *(p_erosion + i*lineBytes + j) = 255; else *(p_erosion + i*lineBytes + j) = 0; } } char writePath[] = "D:\\C++_file\\image_deal_C++\\image_morphology\\photo\\erosion.bmp"; saveBmp(writePath, p_erosion, bmpWidth, bmpHeight, biBitCount, pColorTable); printf("腐蝕完成,請檢視bmp檔案。\n\n"); } int main() { int tembuf[4][4] = { 1, 1,1, 1, 1, 1, 1, 1, 1 ,1,1,1,1};//腐蝕模板 img_erosion(*tembuf, 4,4); system("pause"); return 0; }
2、膨脹
在形態學中,膨脹操作是腐蝕操作的逆運算,因此可以通過對原影象的補集進行腐蝕來得到膨脹後的影象。影象A被結構元B膨脹用集合論來表示如下式:
其中,Ac為A的補集。上式表示結構元對影象A進行膨脹的結果集合是先將B相對原點旋轉180度得到-B,然後使用-B對Ac進行腐蝕,最後求補集。還可用D(A,B)表示膨脹。
作影象膨脹處理時,如果結構元素中只要有一個及以上白色點與它對應的大影象素點相同,該點為白色,否則為黑色。也就是說,如果結構元素中的所有白色點與它對應的大影象素點沒有一個相同,該點為黑色,否則為白色。結構元素中的所有白色點與它對應的大影象素點沒有一個相同,說明大圖的這些畫素點都是黑色的,假如二值圖的骨架為黑色點,這個對白色骨架二值圖的膨脹處理恰好是對黑色骨架二值圖的腐蝕處理。同理,對白色骨架二值圖的腐蝕處理也就是對黑色骨架的膨脹處理。
//-------------------------------膨脹操作------------------------------------// void img_dilate(int *TempBuf, int TempH, int TempW) { char readPath[] = "D:\\C++_file\\image_deal_C++\\image_morphology\\photo\\lunpan.bmp"; readBmp(readPath); unsigned char *pGrayData; //因為轉換後多了個顏色表,所以要改變 bfoffbits += (sizeof(RGBQUAD) * 256); //biSizeImg儲存的為點陣圖資料佔用的位元組數,轉換為灰度影象後值發生改變, //因為24為真彩色點陣圖資料的一個畫素用3各位元組表示,灰度影象為1個位元組 biBitCount = 8; int lineBytes = (bmpWidth * biBitCount / 8 + 3) / 4 * 4; int oldLineBytes = (bmpWidth * 24 / 8 + 3) / 4 * 4;; pGrayData = new unsigned char[lineBytes * bmpHeight]; //memset(pGrayData, 0, bmpInfoHeader.biSizeImage); //-------------------------------定義灰度影象的顏色表--------------------// pColorTable = new RGBQUAD[256]; for (int i = 0; i < 256; i++) { (*(pColorTable + i)).rgbBlue = i; (*(pColorTable + i)).rgbGreen = i; (*(pColorTable + i)).rgbRed = i; (*(pColorTable + i)).rgbReserved = 0; } //--------------------------------將RGB轉換為灰度值------------------------// int red, green, blue; BYTE gray; //char gray_1; int threshold = 128; for (int i = 0; i < bmpHeight; i++) { for (int j = 0; j < bmpWidth; j++) { red = *(pBmpBuf + i*oldLineBytes + 3 * j + 2); green = *(pBmpBuf + i*oldLineBytes + 3 * j + 1); blue = *(pBmpBuf + i*oldLineBytes + 3 * j); gray = (BYTE)((77 * red + 151 * green + 28 * blue) >> 8); //gray_1 = red*0.299 + green*0.587 + blue*0.114; *(pGrayData + i*lineBytes + j) = gray; if (*(pGrayData + i*lineBytes + j)>threshold)//膨脹操作是腐蝕操作的逆運算,因此可以通過對原影象的補集進行腐蝕來得到膨脹後的影象。此處將白色為前景的圖改為黑色為前景 *(pGrayData + i*lineBytes + j) = 255; else *(pGrayData + i*lineBytes + j) = 0; } } unsigned char *p_dilate; p_dilate = new unsigned char[lineBytes * bmpHeight]; memcpy(p_dilate, pGrayData, lineBytes*bmpHeight); int flag; for (int i = TempH / 2; i<bmpHeight - TempH / 2; i++) { for (int j = TempW / 2; j<bmpWidth - TempW / 2; j++) { flag = 1; for (int k = -TempH / 2; k <= TempH / 2; k++) { for (int l = -TempW / 2; l <= TempW / 2; l++) { if (TempBuf[(k + TempH / 2)*TempW + l + TempW / 2]) { if (!*(pGrayData + (i + k)*lineBytes + j + l)) flag = 0; } } } if (flag) *(p_dilate + i*lineBytes + j) = 255; else *(p_dilate + i*lineBytes + j) = 0; } } for (int i = 0; i<bmpHeight; i++)//重新改回以白色為前景 { for (int j = 0; j<bmpWidth; j++) { *(pGrayData + i*lineBytes + j) = 255 - *(pGrayData + i*lineBytes + j); } } for (int i = 0; i<bmpHeight; i++) { for (int j = 0; j<bmpWidth; j++) { *(p_dilate + i*lineBytes + j) = 255 - *(p_dilate + i*lineBytes + j); } } char writePath[] = "D:\\C++_file\\image_deal_C++\\image_morphology\\photo\\dilate.bmp"; saveBmp(writePath, p_dilate, bmpWidth, bmpHeight, biBitCount, pColorTable); printf("膨脹完成,請檢視bmp檔案。\n\n"); }
3、開運算
開操作是先對影象進行腐蝕然後進行膨脹,公式如下:
4、閉運算
閉操作是先對影象進行膨脹然後進行腐蝕。
示例:本文以RGB影象lunpan.bmp為例,先生成白色前景的二值圖erzhi_white.bmp,再分別進行腐蝕和膨脹得到erosion.bmp和dilate.bmp: