本篇所有代码都是基于24位BMP图像
一. 灰度级腐蚀运算
灰度级腐蚀运算可以看成一种特殊的二维卷积运算,只不过用最小值运算代替了相关运算,用减法运算代替了相关运算的卷积操作。
灰度腐蚀运算时逐点进行的,计算该点局部范围内各点与结构元素对应点的灰度差,并选取差的最小值,作为该点的腐蚀结果。(先赋值255,再赋值差的最小值)
1.如果结构元素都是正的,则输出图像会比输入图像暗。
我们设结构元素3*3,且值都是0,看看代码
MFC中添加Menu,添加类向导
void CImageProcessingView::OnXtxHdfs()
{
// TODO: 在此添加命令处理程序代码
if (numPicture == 0)
{
AfxMessageBox("请输入一张图像", MB_OK, 0);
return;
}
if (m_nBitCount != 24)
{
AfxMessageBox("输入图片不是24位", MB_OK, 0);
return;
}
AfxMessageBox("灰度图像腐蚀!", MB_OK, 0);
int num;//记录每一行需要填充的字节
if (m_nWidth * 3 % 4 != 0)
{
num = 4 - m_nWidth * 3 % 4;
}
else
{
num = 0;
}
//打开临时的图片
FILE *fpo = fopen(BmpName, "rb");
FILE *fpw = fopen(BmpNameLin, "wb+");
fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
fread(m_pImage, m_nImage, 1, fpo);
unsigned char *ImageSize;
ImageSize = new unsigned char[m_nImage];
int x, y, val,xx,yy,temp;
for (y = 1; y < m_nHeight - 1; y++)
{
for (x = 1; x < m_nWidth - 1; x++)
//由于使用3*3的结构元素,防止越界,不处理最左边和最右边的的像素
{
val = 255;
for (int j = 0; j < 3; j++)
{
yy = y + j - 1;
for (int i = 0; i < 3; i++)
{
xx = x + i - 1;
if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] < val)//因为结构元素都是0,所以原图像中各点与对应结构元素想减还是原图像本身,
//找想减后差的最小值作为点(x,y)的灰度
{
val = m_pImage[(xx + yy*m_nWidth) * 3 + yy * num];
}
}
}
ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
}
}
fwrite(ImageSize, m_nImage, 1, fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level = 400;
Invalidate();
}
看看结果
可以发现,黑的地方更黑了。
二. 灰度级膨胀运算
灰度级膨胀运算和腐蚀相反,是将原图像各点与对应结构元素中各相加,把相加后的最大值作为目标图像中当前点的灰度值
(先赋值0,再赋值最大值)
MFC中添加Menu,添加类向导,代码如下
void CImageProcessingView::OnXtxHdpz()
{
// TODO: 在此添加命令处理程序代码
if (numPicture == 0)
{
AfxMessageBox("请输入一张图像", MB_OK, 0);
return;
}
if (m_nBitCount != 24)
{
AfxMessageBox("输入图片不是24位", MB_OK, 0);
return;
}
AfxMessageBox("灰度图像膨胀!", MB_OK, 0);
int num;//记录每一行需要填充的字节
if (m_nWidth * 3 % 4 != 0)
{
num = 4 - m_nWidth * 3 % 4;
}
else
{
num = 0;
}
//打开临时的图片
FILE *fpo = fopen(BmpName, "rb");
FILE *fpw = fopen(BmpNameLin, "wb+");
fread(&bfh, sizeof(BITMAPFILEHEADER), 1, fpo);
fread(&bih, sizeof(BITMAPINFOHEADER), 1, fpo);
fwrite(&bfh, sizeof(BITMAPFILEHEADER), 1, fpw);
fwrite(&bih, sizeof(BITMAPINFOHEADER), 1, fpw);
fread(m_pImage, m_nImage, 1, fpo);
unsigned char *ImageSize;
ImageSize = new unsigned char[m_nImage];
int x, y, val, xx, yy, temp;
for (y = 1; y < m_nHeight - 1; y++)
{
for (x = 1; x < m_nWidth - 1; x++)
//由于使用3*3的结构元素,防止越界,不处理最左边和最右边的的像素
{
val = 0;
for (int j = 0; j < 3; j++)
{
yy = y + j - 1;
for (int i = 0; i < 3; i++)
{
xx = x + i - 1;
if (m_pImage[(xx + yy*m_nWidth) * 3 + yy * num] > val)//因为结构元素都是0,所以原图像中各点与对应结构元素想加还是原图像本身,
//找和的最大值作为点(x,y)的灰度
{
val = m_pImage[(xx + yy*m_nWidth) * 3 + yy * num];
}
}
}
ImageSize[(x + y*m_nWidth) * 3 + y*num] = unsigned char(val);
ImageSize[(x + y*m_nWidth) * 3 + y*num + 1] = unsigned char(val);
ImageSize[(x + y*m_nWidth) * 3 + y*num + 2] = unsigned char(val);
}
}
fwrite(ImageSize, m_nImage, 1, fpw);
fclose(fpo);
fclose(fpw);
numPicture = 2;
level = 400;
Invalidate();
}
看看结果
可以看到,黑的变细了,白圈变大了。
三. 灰度开运算
先腐蚀,再膨胀。可以去除相对于结构元素较小的明亮细节,保持整体的灰度和较大的明亮区域不变
四. 灰度闭运算
先膨胀,再腐蚀。可以去除相对于结构元素较小的暗细节,保持整体的灰度和较大的暗区域不变
五. 顶帽变换和底帽运算
顶帽变换通俗的讲就是原图减去开运算(先腐蚀,后膨胀,去白变黑)后的结果。而底帽运算闭运算(先膨胀,再腐蚀,去黑变白)减去原图的结果