数字图像处理(九)图像分割之canny边缘检测

本篇所有代码都是基于24位BMP图像。

且本篇代码只能算canny边缘检测的思路,可能离标准的canny边缘检测还差点。

1)        以低的错误率检测边缘,也即意味着需要尽可能准确的捕获图像中尽可能多的边缘。

2)        检测到的边缘应精确定位在真实边缘的中心。

3)        图像中给定的边缘应只被标记一次,并且在可能的情况下,图像的噪声不应产生假的边缘。

算法步骤

一、用一个高斯滤波器,平滑图像,去除噪声;

二、计算梯度幅值以及梯度角度(也叫方向),即计算图像每个点的强度和方向。这

边需要开一个内存缓冲区,用来保存角度;

三、对梯度幅值使用非最大抑制,以消除边缘检测带来的杂散响应。这边可以看做精

确定位真实边缘的中心;

四、双阈值处理以及连接分析来检测并连接边缘。

基于MFC代码

首先,建立menu类向导,然后代码开始时

void CImageProcessingView::OnXtxFs()  
{  
    // TODO: 在此添加命令处理程序代码  
    if (numPicture == 0)  
    {  
        AfxMessageBox("请输入一张图像", MB_OK, 0);  
        return;  
    }  
  
    if (m_nBitCount != 24)  
    {  
        AfxMessageBox("输入图片不是24位", MB_OK, 0);  
        return;  
    }  
    AfxMessageBox("图像分割,canny!", MB_OK, 0);  

    int num;//记录每一行需要填充的字节  
    if (m_nWidth * 3 % 4 != 0)  
    {  
        num = 4 - m_nWidth * 3 % 4;  
    }  
    else  
    {  
        num = 0;  
    }  
  

一. 高斯滤波器

 关于高斯滤波器,可以看这篇对高斯滤波器的介绍。https://blog.csdn.net/hjxu2016/article/details/80745115

本文的滤波器设置的很简单。设置为


滤波代码如下

float H1[3][3] = {
		{ 1.0 / 16,2.0 / 16,1.0 / 16 }, //模板二:系数1/16  高斯滤波器 
		{ 2.0 / 16,4.0 / 16,2.0 / 16 },
		{ 1.0 / 16,2.0 / 16,1.0 / 16 } };
	int HWS = 3;
	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];//new可以有效的内存释放  
	float red, green, blue;
	int x = 0, y = 0;//代表宽和高的位置  
	int LR, LG, LB;//记录实际的位置  
	int xx, yy;
	int num;//记录需要填充的字节  
	if (m_nWidth * 3 % 4 != 0)
	{
		num = 4 - m_nWidth * 3 % 4;
	}
	else if (m_nWidth * 3 % 4 == 0)
	{
		num = 0;
	}

	//高斯平滑,去除噪声,这里我选择最简单的高斯滤波器
	for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
	{
		for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
		{
			red = green = blue = 0;

			LR = (x + y*m_nWidth) * 3 + y*num;
			//LG = (x + y*m_nWidth) * 3 + 1;  
			//LB = (x + y*m_nWidth) * 3 + 2;  

			for (int j = 0; j < HWS; j++)//第几行  
			{
				yy = y + j - 1;
				for (int i = 0; i < HWS; i++)//第几列  
				{
					xx = x + i - 1;
					red += H1[j][i] * (float)(m_pImage[(xx + yy*m_nWidth) * 3 + yy * num]);
					green += H1[j][i] * (float)(m_pImage[(xx + yy*m_nWidth) * 3 + 1 + yy * num]);
					blue += H1[j][i] * (float)(m_pImage[(xx + yy*m_nWidth) * 3 + 2 + yy * num]);
				}
			}

			ImageSize[LR] = (unsigned char)(red);
			ImageSize[LR + 1] = (unsigned char)(green);
			ImageSize[LR + 2] = (unsigned char)(blue);
		}
	}

二. 计算梯度强度和方向

图像中的边缘可以指向各个方向,因此Canny算法使用四个算子来检测图像中的水平、垂直和对角边缘。边缘检测的算子(如Roberts,Prewitt,Sobel等)返回水平Gx和垂直Gy方向的一阶导数值,由此便可以确定像素点的梯度G和方向theta 。

其中G为梯度强度, theta表示梯度方向,arctan为反正切函数。下面以Sobel算子为例讲述如何计算梯度强度和方向。

梯度角度θ范围从弧度-PI/2到pi/2,我把角度加了PI/2然后把它近似到四个方向,分别代表水平,垂直和两个对角线方向(0°,45°,90°,135°)。我尽量把角度归到四个方向,PI/(PI/4)=(0,1,2,3),落在每个区域的梯度角给一个特定值,代表四个方向之一。

我采用sobel计算梯度和方向,注意,要再申请个缓冲区保存方向

若图像中一个3x3的窗口为A,要计算梯度的像素点为e,则和Sobel算子进行卷积之后,像素点e在x和y方向的梯度值分别为: 

 


代码如下

//计算梯度幅值和角度方向,我申请了一个和ImageSize一样大的空间存放角度,这里我采用sobel边缘检测算子
	//且在计算方向时,我尽量把角度归到四个方向,0,45,90,135.  PI/(PI/4)=(0,1,2,3),这样可以看成是
	//一个八领域,其实更精确的计算是算出双线性插值,为了代码简单,就直接比较像素了,不插值了, 
	//sobel算子
	unsigned char *ImageSizeSobel;//梯度
	ImageSizeSobel = new unsigned char[m_nImage];

	unsigned char *ImageSizeSobelDirection;//方向即角度
	ImageSizeSobelDirection = new unsigned char[m_nImage];

	int HX[3][3] = { { -1,-2,-1 },{ 0,0,0, },{ 1,2,1 } };
	int HY[3][3] = { { -1,0,1 },{ -2,0,2 },{ -1,0,1 } };
	int redX, greenX, blueX;
	int redY, greenY, blueY;
	int R, G, B;
	int a;//方向(0.1.2.3)
	for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
	{
		for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
		{

			redX = greenX = blueX = 0;
			redY = greenY = blueY = 0;
			LR = (y*m_nWidth + x) * 3 + y*num;

			for (int j = 0; j < HWS; j++)//第几行这两个求sobel的dx和dy
			{
				yy = y + j - 1;
				for (int i = 0; i < HWS; i++)//第几列  
				{
					xx = x + i - 1;
					redX += HX[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + yy*num]);
					greenX += HX[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + 1 + yy*num]);
					blueX += HX[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + 2 + yy*num]);

					redY += HY[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + yy*num]);
					greenY += HY[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + 1 + yy*num]);
					blueY += HY[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + 2 + yy*num]);

				}
			}
			double PI = asin(0.5) * 6;
			R = (int)sqrt(redX*redX*1.0 + redY*redY*1.0);
			G = (int)sqrt(greenX*greenX*1.0 + greenY*greenY*1.0);
			B = (int)sqrt(blueX*blueX*1.0 + blueY*blueY*1.0);

			a = int((atan(1.0*redX / redY)+PI/2)/(PI/4));
			ImageSizeSobelDirection[LR] = unsigned char(a);

			if (R > 255) R = 255;
			ImageSizeSobel[LR] = unsigned char(R);
			if (G > 255)G = 255;
			ImageSizeSobel[LR + 1] = unsigned char(G);
			if (B > 255)B = 255;
			ImageSizeSobel[LR + 2] = unsigned char(B);
		}
	}

三. 非极大值抑制

非最大值抑制是一种边缘细化方法。通常得出来的梯度边缘不止一个像素宽,而是多个像素宽。就像我们所说Sobel算子得出来的边缘粗大而明亮,从上面Lena图的Sobel结果可以看得出来。因此这样的梯度图还是很“模糊”。而准则3要求,边缘只有一个精确的点宽度。非最大值抑制能帮助保留局部最大梯度而抑制所有其他梯度值。这意味着只保留了梯度变化中最锐利的位置。算法如下:

  1. 比较当前点的梯度强度和正负梯度方向点的梯度强度。
  2. 如果当前点的梯度强度和同方向的其他点的梯度强度相比较是最大,保留其值。否则抑制,即设为0。比如当前点的方向指向正上方90°方向,那它需要和垂直方向,它的正上方和正下方的像素比较。

注意,方向的正负是不起作用的,比如东南方向和西北方向是一样的,都认为是对角线的一个方向。前面我们把梯度方向近似到水平,垂直和两个对角线四个方向,所以每个像素根据自身方向在这四个方向之一进行比较,决定是否保留。这一部分的代码也很简单,列出如下。pModule,pDirection分别记录了上一步梯度模值和梯度方向。


//非最大值抑制,直接对ImageSizeSobel处理
	int LRO, LR1, LR2, LR3, LR4, LR5, LR6, LR7;//逆时针开始八个方向的坐标

	for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
	{
		for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
		{

			LR = (y*m_nWidth + x) * 3 + y*num;
			LRO = (y*m_nWidth + x + 1) * 3 + y*num;
			LR1 = ((y + 1)*m_nWidth + x + 1) * 3 + (y + 1)*num;
			LR2 = ((y + 1)*m_nWidth + x) * 3 + (y + 1)*num;
			LR3 = ((y + 1)*m_nWidth + x - 1) * 3 + (y + 1)*num;
			LR4 = ((y)*m_nWidth + x - 1) * 3 + (y)*num;
			LR5 = ((y - 1)*m_nWidth + x - 1) * 3 + (y - 1)*num;
			LR6 = ((y - 1)*m_nWidth + x) * 3 + (y - 1)*num;
			LR6 = ((y - 1)*m_nWidth + x + 1) * 3 + (y - 1)*num;

			switch (ImageSizeSobelDirection[LR])//这里面的值是0.1.2.3,对应着四个角度
			{
			case 0:
				if (ImageSizeSobel[LR] <= ImageSizeSobel[LRO] || ImageSizeSobel[LR] <= ImageSizeSobel[LR4])
				{
					ImageSizeSobel[LR] = 0;
					ImageSizeSobel[LR+1] = 0;
					ImageSizeSobel[LR + 2] = 0;
					break;
				}
			case 1:
				if (ImageSizeSobel[LR] <= ImageSizeSobel[LR1] || ImageSizeSobel[LR] <= ImageSizeSobel[LR5])
				{
					ImageSizeSobel[LR] = 0;
					ImageSizeSobel[LR + 1] = 0;
					ImageSizeSobel[LR + 2] = 0;
					break;
				}

			case 2:
				if (ImageSizeSobel[LR] <= ImageSizeSobel[LR2] || ImageSizeSobel[LR] <= ImageSizeSobel[LR6])
				{
					ImageSizeSobel[LR] = 0;
					ImageSizeSobel[LR + 1] = 0;
					ImageSizeSobel[LR + 2] = 0;
					break;
				}
			case 3:
				if (ImageSizeSobel[LR] <= ImageSizeSobel[LR3] || ImageSizeSobel[LR] <= ImageSizeSobel[LR7])
				{
					ImageSizeSobel[LR] = 0;
					ImageSizeSobel[LR + 1] = 0;
					ImageSizeSobel[LR + 2] = 0;
					break;
				}
			}
		}
	}

四. 双阈值处理

一个高阈值和低阈值,高于高阈值的为255,低于低阈值的为0,一般取阈值为图像整体灰度的70%,这里我直接偷懒,分别设为100和30

	//双阈值处理,一个高阈值,一个低阈值,通常高阈值是低阈值的1.5-2倍。高于高阈值的为255,低于低阈值的为0,这里我们设置阈值为100和30.
	int H0 = 100, L0 = 30;
	for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
	{
		for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
		{

			LR = (y*m_nWidth + x) * 3 + y*num;
			if (ImageSizeSobel[LR] > H0)
			{
				ImageSizeSobel[LR] = 255;
				ImageSizeSobel[LR + 1] = 255;
				ImageSizeSobel[LR + 2] = 255;
			}
			if (ImageSizeSobel[LR] < L0)
			{
				ImageSizeSobel[LR] = 0;
				ImageSizeSobel[LR + 1] = 0;
				ImageSizeSobel[LR + 2] = 0;
			}
		}
	}

五、连接处理(孤立点抑制和连接)

灰度在低阈值和高阈值之间的,需要考察该点临近的8像素是否有灰度值为255的,如果没有,表示这是一个孤立的局部极大值点,可以排除了,赋值为0;如果只要有一个点是255,那么这个点是和其他边缘有连接的点,不能排除,赋值为255。

//链接处理,灰度在低阈值和高阈值之间的,考察改点临近的灰度值是否有255的,如果没有,赋值0,如果右,赋值255

	for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
	{
		for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
		{
			LR = (x + y*m_nWidth) * 3 + y*num;
			if (ImageSizeSobel[LR] >= L0 && ImageSizeSobel[LR] <= H0)//判断像素是不是在阈值区间
			{
				int temp = 0;
				for (int j = 0; j < HWS; j++)//第几行  
				{
					yy = y + j - 1;
					for (int i = 0; i < HWS; i++)//第几列  
					{
						xx = x + i - 1;
						if (ImageSizeSobel[(xx + yy*m_nWidth) * 3 + yy * num] == 255)

						{
							temp = 255;
							break;
						}

					}
					if (temp == 255) break;
				}

				ImageSizeSobel[LR] = (unsigned char)(temp);
				ImageSizeSobel[LR + 1] = (unsigned char)(temp);
				ImageSizeSobel[LR + 2] = (unsigned char)(temp);
			}
		}
	}


	fwrite(ImageSizeSobel, m_nImage, 1, fpw);
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level = 400;
	Invalidate();
}

完整版代码如下

void CImageProcessingView::OnTxfgCanny()
{
	// TODO: 在此添加命令处理程序代码
	if (numPicture == 0)
	{
		AfxMessageBox("请输入一张图片", MB_OK, 0);
		return;
	}
	if (m_nBitCount != 24)
	{
		AfxMessageBox("输入图片不是24位", MB_OK, 0);
		return;
	}
	AfxMessageBox("图像分割,canny边缘检测", MB_OK, 0);
	float H1[3][3] = {
		{ 1.0 / 16,2.0 / 16,1.0 / 16 }, //模板二:系数1/16  高斯滤波器 
		{ 2.0 / 16,4.0 / 16,2.0 / 16 },
		{ 1.0 / 16,2.0 / 16,1.0 / 16 } };
	int HWS = 3;
	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];//new可以有效的内存释放  
	float red, green, blue;
	int x = 0, y = 0;//代表宽和高的位置  
	int LR, LG, LB;//记录实际的位置  
	int xx, yy;
	int num;//记录需要填充的字节  
	if (m_nWidth * 3 % 4 != 0)
	{
		num = 4 - m_nWidth * 3 % 4;
	}
	else if (m_nWidth * 3 % 4 == 0)
	{
		num = 0;
	}

	//高斯平滑,去除噪声,这里我选择最简单的高斯滤波器
	for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
	{
		for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
		{
			red = green = blue = 0;

			LR = (x + y*m_nWidth) * 3 + y*num;
			//LG = (x + y*m_nWidth) * 3 + 1;  
			//LB = (x + y*m_nWidth) * 3 + 2;  

			for (int j = 0; j < HWS; j++)//第几行  
			{
				yy = y + j - 1;
				for (int i = 0; i < HWS; i++)//第几列  
				{
					xx = x + i - 1;
					red += H1[j][i] * (float)(m_pImage[(xx + yy*m_nWidth) * 3 + yy * num]);
					green += H1[j][i] * (float)(m_pImage[(xx + yy*m_nWidth) * 3 + 1 + yy * num]);
					blue += H1[j][i] * (float)(m_pImage[(xx + yy*m_nWidth) * 3 + 2 + yy * num]);
				}
			}

			ImageSize[LR] = (unsigned char)(red);
			ImageSize[LR + 1] = (unsigned char)(green);
			ImageSize[LR + 2] = (unsigned char)(blue);
		}
	}

	//计算梯度幅值和角度方向,我申请了一个和ImageSize一样大的空间存放角度,这里我采用sobel边缘检测算子
	//且在计算方向时,我尽量把角度归到四个方向,0,45,90,135.  PI/(PI/4)=(0,1,2,3),这样可以看成是
	//一个八领域,其实更精确的计算是算出双线性插值,为了代码简单,就直接比较像素了,不插值了, 
	//sobel算子
	unsigned char *ImageSizeSobel;
	ImageSizeSobel = new unsigned char[m_nImage];

	unsigned char *ImageSizeSobelDirection;
	ImageSizeSobelDirection = new unsigned char[m_nImage];

	int HX[3][3] = { { -1,-2,-1 },{ 0,0,0, },{ 1,2,1 } };
	int HY[3][3] = { { -1,0,1 },{ -2,0,2 },{ -1,0,1 } };
	int redX, greenX, blueX;
	int redY, greenY, blueY;
	int R, G, B;
	int a;//方向(0.1.2.3)
	for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
	{
		for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
		{

			redX = greenX = blueX = 0;
			redY = greenY = blueY = 0;
			LR = (y*m_nWidth + x) * 3 + y*num;

			for (int j = 0; j < HWS; j++)//第几行这两个循环是求权重后的均值  
			{
				yy = y + j - 1;
				for (int i = 0; i < HWS; i++)//第几列  
				{
					xx = x + i - 1;
					redX += HX[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + yy*num]);
					greenX += HX[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + 1 + yy*num]);
					blueX += HX[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + 2 + yy*num]);

					redY += HY[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + yy*num]);
					greenY += HY[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + 1 + yy*num]);
					blueY += HY[j][i] * (ImageSize[(xx + yy*m_nWidth) * 3 + 2 + yy*num]);

				}
			}
			double PI = asin(0.5) * 6;
			R = (int)sqrt(redX*redX*1.0 + redY*redY*1.0);
			G = (int)sqrt(greenX*greenX*1.0 + greenY*greenY*1.0);
			B = (int)sqrt(blueX*blueX*1.0 + blueY*blueY*1.0);

			a = int((atan(1.0*redX / redY)+PI/2)/(PI/4));
			ImageSizeSobelDirection[LR] = unsigned char(a);

			if (R > 255) R = 255;
			ImageSizeSobel[LR] = unsigned char(R);
			if (G > 255)G = 255;
			ImageSizeSobel[LR + 1] = unsigned char(G);
			if (B > 255)B = 255;
			ImageSizeSobel[LR + 2] = unsigned char(B);
		}
	}

	//非最大值抑制,直接对ImageSizeSobel处理
	int LRO, LR1, LR2, LR3, LR4, LR5, LR6, LR7;//八个方向

	for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
	{
		for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
		{

			LR = (y*m_nWidth + x) * 3 + y*num;
			LRO = (y*m_nWidth + x + 1) * 3 + y*num;
			LR1 = ((y + 1)*m_nWidth + x + 1) * 3 + (y + 1)*num;
			LR2 = ((y + 1)*m_nWidth + x) * 3 + (y + 1)*num;
			LR3 = ((y + 1)*m_nWidth + x - 1) * 3 + (y + 1)*num;
			LR4 = ((y)*m_nWidth + x - 1) * 3 + (y)*num;
			LR5 = ((y - 1)*m_nWidth + x - 1) * 3 + (y - 1)*num;
			LR6 = ((y - 1)*m_nWidth + x) * 3 + (y - 1)*num;
			LR6 = ((y - 1)*m_nWidth + x + 1) * 3 + (y - 1)*num;

			switch (ImageSizeSobelDirection[LR])
			{
			case 0:
				if (ImageSizeSobel[LR] <= ImageSizeSobel[LRO] || ImageSizeSobel[LR] <= ImageSizeSobel[LR4])
				{
					ImageSizeSobel[LR] = 0;
					ImageSizeSobel[LR+1] = 0;
					ImageSizeSobel[LR + 2] = 0;
					break;
				}
			case 1:
				if (ImageSizeSobel[LR] <= ImageSizeSobel[LR1] || ImageSizeSobel[LR] <= ImageSizeSobel[LR5])
				{
					ImageSizeSobel[LR] = 0;
					ImageSizeSobel[LR + 1] = 0;
					ImageSizeSobel[LR + 2] = 0;
					break;
				}

			case 2:
				if (ImageSizeSobel[LR] <= ImageSizeSobel[LR2] || ImageSizeSobel[LR] <= ImageSizeSobel[LR6])
				{
					ImageSizeSobel[LR] = 0;
					ImageSizeSobel[LR + 1] = 0;
					ImageSizeSobel[LR + 2] = 0;
					break;
				}
			case 3:
				if (ImageSizeSobel[LR] <= ImageSizeSobel[LR3] || ImageSizeSobel[LR] <= ImageSizeSobel[LR7])
				{
					ImageSizeSobel[LR] = 0;
					ImageSizeSobel[LR + 1] = 0;
					ImageSizeSobel[LR + 2] = 0;
					break;
				}
			}
		}
	}

	//双阈值处理,一个高阈值,一个低阈值,通常高阈值是低阈值的1.5-2倍。高于高阈值的为255,低于低阈值的为0,这里我们设置阈值为100和30.
	int H0 = 100, L0 = 30;
	for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
	{
		for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
		{

			LR = (y*m_nWidth + x) * 3 + y*num;
			if (ImageSizeSobel[LR] > H0)
			{
				ImageSizeSobel[LR] = 255;
				ImageSizeSobel[LR + 1] = 255;
				ImageSizeSobel[LR + 2] = 255;
			}
			if (ImageSizeSobel[LR] < L0)
			{
				ImageSizeSobel[LR] = 0;
				ImageSizeSobel[LR + 1] = 0;
				ImageSizeSobel[LR + 2] = 0;
			}
		}
	}

	//链接处理,灰度在低阈值和高阈值之间的,考察改点临近的灰度值是否有255的,如果没有,赋值0,如果右,赋值255

	for (x = HWS / 2; x < m_nWidth - HWS / 2; x++)
	{
		for (y = HWS / 2; y < m_nHeight - HWS / 2; y++)
		{
			LR = (x + y*m_nWidth) * 3 + y*num;
			if (ImageSizeSobel[LR] >= L0 && ImageSizeSobel[LR] <= H0)//判断像素是不是在阈值区间
			{
				int temp = 0;
				for (int j = 0; j < HWS; j++)//第几行  
				{
					yy = y + j - 1;
					for (int i = 0; i < HWS; i++)//第几列  
					{
						xx = x + i - 1;
						if (ImageSizeSobel[(xx + yy*m_nWidth) * 3 + yy * num] == 255)

						{
							temp = 255;
							break;
						}

					}
					if (temp == 255) break;
				}

				ImageSizeSobel[LR] = (unsigned char)(temp);
				ImageSizeSobel[LR + 1] = (unsigned char)(temp);
				ImageSizeSobel[LR + 2] = (unsigned char)(temp);
			}
		}
	}


	fwrite(ImageSizeSobel, m_nImage, 1, fpw);
	fclose(fpo);
	fclose(fpw);
	numPicture = 2;
	level = 400;
	Invalidate();
}

滤波器选择以及方向选择比较随意,所以结果不是很准,就大体了解一下算法的内容,结果如下





  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值