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: