Canny边缘检测

来源:互联网 发布:maya2015软件补丁更新 编辑:程序博客网 时间:2024/05/01 18:33

        关于Canny图像边缘检测的原理,网上有很多介绍的资料,

其中一篇介绍得比较好的文章:http://blog.csdn.net/likezhaobin/article/details/6892176

大家可以参考一下。


----------------------少废话,上代码-------------------------------------------------

/* * CannyEdgeDetection.h * * 过客 && 386520874@qq.com && 2016.03.26 **/#ifndef __CANNY_EDGE_DETECTION_H__#define __CANNY_EDGE_DETECTION_H__#include <windows.h>#include <vector>//----canny边缘检测-----------class CCannyEdgeDetection{public:CCannyEdgeDetection();~CCannyEdgeDetection();public:int m_guid_index; //保存的图片格式,0-ImageFormatBMP,1-ImageFormatJPEG,2-ImageFormatPNG,3-ImageFormatGIFBITMAP m_bitmap;HBITMAP m_hBitmap;public://--------图片操作---------------int OpenImage(wchar_t* filename, BITMAP &bitmap, HBITMAP &hBitmap); //打开图片int SaveImage(wchar_t* filename, HBITMAP &hBitmap); //保存图片,针对位图句柄int SaveImage(wchar_t* filename, BITMAP &bitmap); //保存图片,针对内存中位图结果int CreateEmptyImage(BITMAP &bitmap, int width, int height, int bmBitsPixel); //在内存中创建一幅空白位图int ReleaseHandle(); //主动释放资源int ReleaseBitmap(BITMAP &bitmap); //主动释放资源int Canny(BITMAP &bitmap_src, BITMAP &bitmap_dst, double low_thresh, double high_thresh); //canny边缘检测int Sobel(BITMAP &bitmap_src, BITMAP &bitmap_dst, double low_thresh, double high_thresh); //Sobel图像一阶差分梯度};#endif //__CANNY_EDGE_DETECTION_H__

/* * CannyEdgeDetection.cpp * * 过客 && 386520874@qq.com && 2016.03.26 **/#define WINVER 0x0500 #define _WIN32_WINNT 0x0500//#include <windows.h>#include <afx.h>#include <atlimage.h>#include <afxwin.h>#include "CannyEdgeDetection.h"CCannyEdgeDetection::CCannyEdgeDetection(){m_guid_index = 2; //默认输出pngm_hBitmap = NULL;}CCannyEdgeDetection::~CCannyEdgeDetection(){if(m_hBitmap != NULL){::DeleteObject(m_hBitmap);m_hBitmap = NULL;}}//--------图片操作---------------int CCannyEdgeDetection::ReleaseHandle(){if(m_hBitmap != NULL){::DeleteObject(m_hBitmap);m_hBitmap = NULL;}return 1;}int CCannyEdgeDetection::ReleaseBitmap(BITMAP &bitmap){unsigned char* pBits = static_cast<unsigned char*>(bitmap.bmBits);if(pBits != NULL){delete [] pBits; //释放用new申请的资源pBits = NULL;memset(&bitmap, 0, sizeof(BITMAP));}return 1;}int CCannyEdgeDetection::OpenImage(wchar_t* filename, BITMAP &bitmap, HBITMAP &hBitmap){CFileFind filefind;BOOL IsFileFind = filefind.FindFile(filename);if(!IsFileFind){//printf("Error: Can not find file: %s;\n", filename);wchar_t err_str[200];wsprintf(err_str, _T("您打开的图片文件[%s]不存在!"), filename);MessageBox(NULL, err_str, _T("错误"), MB_OK|MB_ICONERROR);return 0;}//----------------------------------------CImage img;img.Load(filename);int width = img.GetWidth();int height = img.GetHeight();hBitmap = img.Detach(); //如果用Detach(),则CImage析构后,hBitmap仍可使用。int nBytes = ::GetObject(hBitmap, sizeof(BITMAP), &bitmap);return 1;}int CCannyEdgeDetection::SaveImage(wchar_t* filename, HBITMAP &hBitmap){GUID guid[4] ={Gdiplus::ImageFormatBMP,Gdiplus::ImageFormatJPEG,Gdiplus::ImageFormatPNG,Gdiplus::ImageFormatGIF};if(m_guid_index<0 || m_guid_index>3){printf("Erorr: SaveImage: m_guid_index must in [0,3]\n");}CImage img;img.Attach(hBitmap);img.Save(filename, guid[m_guid_index]); //可以从guid看出,CImage的Save()实际上是通过GDI+实现的。hBitmap = img.Detach(); //如果用Detach(),则CImage析构后,hBitmap仍可使用。return 1;}int CCannyEdgeDetection::SaveImage(wchar_t* filename, BITMAP &bitmap){int width = bitmap.bmWidth;int height = bitmap.bmHeight;int biBitCount = bitmap.bmBitsPixel;RGBQUAD *pColorTable = NULL;int colorTablesize = 0; //颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0//if(biBitCount == 8){colorTablesize=1024;}//int lineByte = (width * biBitCount/8+3)/4*4; //待存储图像数据每行字节数为4的倍数int lineByte = bitmap.bmWidthBytes; //待存储图像数据每行字节数为4的倍数int size_1 = sizeof(BITMAPFILEHEADER); // size_1 = 14int size_2 = sizeof(BITMAPINFOHEADER); // size_2 = 40int size_3 = lineByte * height; //计算位图尺寸//int bpp = bitmap.bmBitsPixel/8; //bpp代表通道的数目,一般 bpp = 3//--------------1. 位图文件头结构-----------------------------------------------------BITMAPFILEHEADER fileHead;fileHead.bfType = 0x4D42; //bmp类型fileHead.bfSize = size_1 + size_2 + colorTablesize + lineByte * height; //bfSize是图像文件4个组成部分之和fileHead.bfReserved1 = 0;fileHead.bfReserved2 = 0;fileHead.bfOffBits = 54 + colorTablesize; //bfOffBits是图像文件前3个部分所需空间之和//--------------2. 位图信息头结构-----------------------------------------------------BITMAPINFOHEADER head;head.biBitCount = biBitCount; // 8,24,32head.biClrImportant = 0;head.biClrUsed = 0;head.biCompression = 0; //BI_RGB = 0Lhead.biHeight = height;head.biPlanes = 1;head.biSize = 40;head.biSizeImage = lineByte * height;head.biWidth = width;head.biXPelsPerMeter = 0;head.biYPelsPerMeter = 0;//---------------3. 内存中的文件读写操作-------------------------------long file_size = fileHead.bfSize; //计算位图文件尺寸unsigned char* pBits = static_cast<unsigned char*>(bitmap.bmBits);HGLOBAL hMem = GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT, file_size);if(hMem == NULL){printf("Erorr: GlobalAlloc: hMem == NULL\n");return 0;}unsigned char *pbuff = static_cast<unsigned char*>(GlobalLock(hMem)); // get the actual pointer for the HGLOBALmemcpy(pbuff, &fileHead, size_1); //内存复制pbuff += size_1;memcpy(pbuff, &head, size_2); //内存复制pbuff += size_2;memcpy(pbuff, pBits, size_3); //内存复制IStream *pStream = 0;HRESULT hr = CreateStreamOnHGlobal(hMem, TRUE, &pStream); //此函数是内存数据到文件流的关键API函数if(hr != S_OK){printf("Erorr: CreateStreamOnHGlobal: hr != S_OK\n");return 0;}//--------------4. 将文件流数据正式保存到磁盘文件中----------------------------------------GUID guid[4] ={Gdiplus::ImageFormatBMP,Gdiplus::ImageFormatJPEG,Gdiplus::ImageFormatPNG,Gdiplus::ImageFormatGIF};if(m_guid_index<0 || m_guid_index>3){printf("Erorr: SaveImage: m_guid_index must in [0,3]\n");return 0;}CImage img;img.Load(pStream);img.Save(filename, guid[m_guid_index]); //可以从guid看出,CImage的Save()实际上是通过GDI+实现的。img.Detach();img.Destroy();GlobalFree(hMem); //释放GlobalAlloc(...)申请的内存return 1;}/*-------------------在内存中创建一幅空白位图------------------------------ * * 参数1: bitmap 返回的结果 * 参数2: width 位图高度 * 参数3: height 位图宽度 * 参数4: bmBitsPixel 一个像素的字节大小,一般是24字节,也可以是32字节 * * 过客 && 386520874@qq.com && 2014.12.26*/int CCannyEdgeDetection::CreateEmptyImage(BITMAP &bitmap, int width, int height, int bmBitsPixel){//bmBitsPixel = 32;bitmap.bmWidth = width;bitmap.bmHeight = height;bitmap.bmBitsPixel = bmBitsPixel;bitmap.bmType = 0;bitmap.bmPlanes = 1;bitmap.bmWidthBytes = (width * bmBitsPixel/8+3)/4*4;printf("CreateEmptyImage: [%d x %d] memory = %d bytes;\n", width, height, bitmap.bmHeight * bitmap.bmWidthBytes);unsigned char *pBits = new unsigned char[bitmap.bmHeight * bitmap.bmWidthBytes]; //在堆上申请if(pBits == NULL){printf("CreateEmptyImage: pBits == NULL\n");return 0;}memset(pBits, 0, sizeof(unsigned char) * bitmap.bmHeight * bitmap.bmWidthBytes); //初始化为黑色背景bitmap.bmBits = pBits;return 1;}/*-------------------图像的Canny边缘检测------------------------------ * 算法原理: *     图像的Canny边缘检测算法,是一种理论和实际效果比较靠谱的算法,它 *     大致有下面几个步骤: *     1. 将输入的RGB图像转换成单通道的灰度图像 *     2. 计算灰度图像的一阶梯度,该算法选择Sobel算子计算dx和dy两个方向 *        的梯度 *     3. 对图像的梯度幅值进行非极大值抑制,这一步是Canny算法的独到之处 *        经过这一步后,真正的边缘点会被暴露出来,并将其位置保存下来 *     4. 双阈值检测:将保存下来的已确认为边缘的点,以每个点为中心,将 *        相邻的8个像素由1变成2,即将曲线的轮廓进行不断的延伸。 * *   Sobel算子模板:https://en.wikipedia.org/wiki/Sobel_operator *            | -1  0  +1 |          | -1  -2  -1 | *       Sx = | -2  0  +2 |     Sy = |  0   0   0 | *            | -1  0  +1 |          | +1  +2  +1 | * * 函数名称: Canny(...) * 参数1: bitmap_src [in]输入的图像位图数据 * 参数2: bitmap_dst [out]输出的图像位图数据 * 参数3: low_thresh [in]低阈值,所有梯度幅值低于此值的点不认为是边缘点 * 参数4: high_thresh [in]高阈值,经过非极大值抑制后,所有梯度幅值高于 *                    此值的点认为是边缘点 * * 过客 && 386520874@qq.com && 2015.03.27*/int CCannyEdgeDetection::Canny(BITMAP &bitmap_src, BITMAP &bitmap_dst, double low_thresh, double high_thresh){//-------------1. 输入参数检查-----------------------BITMAP bitmap1 = bitmap_src;unsigned char* pBits1 = static_cast<unsigned char*>(bitmap1.bmBits);int bpp1 = bitmap1.bmBitsPixel/8; //bpp代表通道的数目,一般 bpp = 3int width2 = bitmap1.bmWidth;int height2 = bitmap1.bmHeight;CreateEmptyImage(bitmap_dst, width2, height2, bitmap1.bmBitsPixel);int bpp2 = bitmap_dst.bmBitsPixel/8; //bpp代表通道的数目,一般 bpp = 3unsigned char* pBits2 = static_cast<unsigned char*>(bitmap_dst.bmBits);BITMAP bitmap2 = bitmap_dst;//-------------2. RGB转灰度-----------------------int* gray = new int[width2 * height2]; //保存灰度图像数据for(int y = 0; y < bitmap1.bmHeight; y++){for(int x = 0; x < bitmap1.bmWidth; x++){int B = pBits1[y * bitmap1.bmWidthBytes + x * bpp1 + 0]; //Blueint G = pBits1[y * bitmap1.bmWidthBytes + x * bpp1 + 1]; //Greenint R = pBits1[y * bitmap1.bmWidthBytes + x * bpp1 + 2]; //Red//int A = R * 0.299 + G * 0.587 + B * 0.114; //一般RGB2Gray公式int A = R * 0.212671 + G * 0.715160 + B * 0.072169; //opencv的RGB2Gray公式 0.212671*R + 0.715160*G + 0.072169*Bgray[y * width2 + x] = A;}}//-------------3. 计算灰度图像梯度幅值和方向-----------------------int* dx = new int[width2 * height2]; //x向偏导数int* dy = new int[width2 * height2]; //y向偏导数memset(dx, 0, sizeof(int) * width2 * height2);memset(dy, 0, sizeof(int) * width2 * height2);//利用Sobel算子,计算x,y方向的偏导数for(int y = 0; y < height2; y++){for(int x = 0; x < width2; x++){if(x < 1 || x >= width2 -1 || y < 1 || y >= height2 - 1){continue;} //3x3的算子,图像的4条边需要跳过dx[y * width2 + x] = -(gray[(y - 1) * width2 + (x - 1)] * 1 + gray[(y + 0) * width2 + (x - 1)] * 2 + gray[(y + 1) * width2 + (x - 1)] * 1)+ (gray[(y - 1) * width2 + (x + 1)] * 1 + gray[(y + 0) * width2 + (x + 1)] * 2 + gray[(y + 1) * width2 + (x + 1)] * 1);dy[y * width2 + x] = -(gray[(y - 1) * width2 + (x - 1)] * 1 + gray[(y - 1) * width2 + (x + 0)] * 2 + gray[(y - 1) * width2 + (x + 1)] * 1)+ (gray[(y + 1) * width2 + (x - 1)] * 1 + gray[(y + 1) * width2 + (x + 0)] * 2 + gray[(y + 1) * width2 + (x + 1)] * 1);}}//计算梯度幅值和梯度的方向//.......//-------------4. 非极大值抑制-----------------------//下面代码来自于opencv的canny.cpp修改版本bool L2gradient = false; //采用哪种梯度的计算公式//bool L2gradient = true; //采用哪种梯度的计算公式//const int cn = src.channels();const int cn = 1;int low = low_thresh;int high = high_thresh;typedef unsigned char uchar;ptrdiff_t mapstep = width2 + 2;//注意buffer是一个二维数组,总体来说有[3 + height]行,前面3行用来不断滚动的临时计算//和存储图像梯度|dx+dy|的幅值,后面[height]行中每个数组元素用来标记,图像中对应点是//否是边缘点信息,只有[0,1,2]三种值,//0-表示该像素可能是边缘//1-表示该像素不可能是边缘//2-表示该像素是边缘uchar * buffer = new uchar[(width2 + 2) * (height2 + 2) + cn * mapstep * 3 * sizeof(int)]; //存储边缘信息的数组int* mag_buf[3];mag_buf[0] = (int*)(uchar*)buffer;mag_buf[1] = mag_buf[0] + mapstep * cn;mag_buf[2] = mag_buf[1] + mapstep * cn;memset(mag_buf[0], 0, mapstep * sizeof(int));uchar* map = (uchar*)(mag_buf[2] + mapstep * cn);memset(map, 1, mapstep);memset(map + mapstep * (height2 + 1), 1, mapstep);int maxsize = max(1 << 10, width2 * height2 / 10); //栈stack用来存储标记为2的像素点指针,栈的最大尺寸为[width2 * height2]std::vector<uchar*> stack(maxsize);uchar **stack_top = &stack[0];uchar **stack_bottom = &stack[0];//----------------------------#define CANNY_PUSH(d)    *(d) = uchar(2), *stack_top++ = (d)#define CANNY_POP(d)     (d) = *--stack_top// calculate magnitude and angle of gradient, perform non-maxima suppression.// fill the map with one of the following values://   0 - the pixel might belong to an edge,0-表示该像素可能是边缘//   1 - the pixel can not belong to an edge,1-表示该像素不可能是边缘//   2 - the pixel does belong to an edge,2-表示该像素是边缘for(int i = 0; i <= height2; i++) //遍历行{int* _norm = mag_buf[(i > 0) + 1] + 1;if(i < height2){int* _dx = dx + i * width2;int* _dy = dy + i * width2;if(!L2gradient){for(int j = 0; j < width2 * cn; j++){_norm[j] = abs(_dx[j]) + abs(_dy[j]); //梯度的幅值 |G| = |dx| + |dy|,默认使用这个公式}}else{for(int j = 0; j < width2 * cn; j++){//_norm[j] = _dx[j] * _dx[j] + _dy[j] * _dy[j]; //梯度的幅值 |G| = |dx|*|dx| + |dy|*|dy|_norm[j] = sqrt(1.0 * (_dx[j] * _dx[j] + _dy[j] * _dy[j])); //梯度的幅值 |G| = sqrt(|dx|*|dx| + |dy|*|dy|)}}if(cn > 1){for(int j = 0, jn = 0; j < width2; ++j, jn += cn){int maxIdx = jn;for(int k = 1; k < cn; ++k){if(_norm[jn + k] > _norm[maxIdx]){maxIdx = jn + k;}}_norm[j] = _norm[maxIdx];_dx[j] = _dx[maxIdx];_dy[j] = _dy[maxIdx];}}_norm[-1] = _norm[width2] = 0;}else{memset(_norm - 1, 0, mapstep * sizeof(int));}// at the very beginning we do not have a complete ring// buffer of 3 magnitude rows for non-maxima suppressionif (i == 0){continue;}uchar* _map = map + mapstep * i + 1;_map[-1] = _map[width2] = 1;int* _mag = mag_buf[1] + 1; // take the central rowptrdiff_t magstep1 = mag_buf[2] - mag_buf[1];ptrdiff_t magstep2 = mag_buf[0] - mag_buf[1];const int* _x = dx + (i - 1) * width2;const int* _y = dy + (i - 1) * width2;if((stack_top - stack_bottom) + width2 > maxsize){int sz = (int)(stack_top - stack_bottom);maxsize = maxsize * 3 / 2;stack.resize(maxsize); //将栈空间扩大为原来的3/2=1.5倍stack_bottom = &stack[0];stack_top = stack_bottom + sz;}int prev_flag = 0;for(int j = 0; j < width2; j++) //遍历列{#define CANNY_SHIFT 15const int TG22 = (int)(0.4142135623730950488016887242097*(1<<CANNY_SHIFT) + 0.5); //tan(PI/8)=0.41421356...double m = _mag[j];if(m > low){int xs = _x[j];int ys = _y[j];int x = std::abs(xs);int y = std::abs(ys) << CANNY_SHIFT;double tg22x = x * TG22;if(y < tg22x) // PI/8 = 22.5度,tan(PI/8){if(m > _mag[j-1] && m >= _mag[j+1]) goto __ocv_canny_push; //非极大值抑制:3x3模板,8邻域像素的水平方向}else{double tg67x = tg22x + (x << (CANNY_SHIFT+1));if(y > tg67x) // PI*3/8 = 67.5度,tan(PI*3/8){if(m > _mag[j+magstep2] && m >= _mag[j+magstep1]) goto __ocv_canny_push; //非极大值抑制:3x3模板,8邻域像素的垂直方向}else{int s = (xs ^ ys) < 0 ? -1 : 1;if(m > _mag[j+magstep2-s] && m > _mag[j+magstep1+s]) goto __ocv_canny_push; //非极大值抑制:3x3模板,8邻域像素的两条45度对角线方向}}}prev_flag = 0;_map[j] = uchar(1); //1-表示该像素点不是边缘continue;__ocv_canny_push:if (!prev_flag && m > high && _map[j-mapstep] != 2){CANNY_PUSH(_map + j); //2-表示该像素是边缘,则将其弹入栈中prev_flag = 1;}else{_map[j] = 0; //0-表示该像素可能是边缘}}// scroll the ring buffer// 滚动交换保存梯度幅值的行_mag = mag_buf[0];mag_buf[0] = mag_buf[1];mag_buf[1] = mag_buf[2];mag_buf[2] = _mag;}//-------------5. 双阈值检测:将8个相邻的像素由1变成2----------------------------------// now track the edges (hysteresis thresholding)while(stack_top > stack_bottom){uchar* m;if ((stack_top - stack_bottom) + 8 > maxsize){int sz = (int)(stack_top - stack_bottom);maxsize = maxsize * 3 / 2;stack.resize(maxsize); //将栈空间扩大为原来的3/2=1.5倍stack_bottom = &stack[0];stack_top = stack_bottom + sz;}CANNY_POP(m);//因为栈中保存的都是边缘点像素,现在循环检测栈中所有边缘点的//周围8个像素是否被标记为0,如果是0,则认为该点也是边缘点,并//将其弹入栈中,可以看出这个栈的功能实现了递归函数的功能,直//到栈空为止,则结束循环。if(!m[-1])         CANNY_PUSH(m - 1);if(!m[1])          CANNY_PUSH(m + 1);if(!m[-mapstep-1]) CANNY_PUSH(m - mapstep - 1);if(!m[-mapstep])   CANNY_PUSH(m - mapstep);if(!m[-mapstep+1]) CANNY_PUSH(m - mapstep + 1);if(!m[mapstep-1])  CANNY_PUSH(m + mapstep - 1);if(!m[mapstep])    CANNY_PUSH(m + mapstep);if(!m[mapstep+1])  CANNY_PUSH(m + mapstep + 1);}//------------6. 保存算法结果到图片中----------------------// the final pass, form the final imageconst uchar* pmap = map + mapstep + 1;for(int y = 0; y < height2; y++){for(int x = 0; x < width2; x++){uchar _gray = (uchar)-(pmap[y * mapstep + x] >> 1); //因为pmap[]里面的值只有0,1,2三种,而2才是边缘像素点,所以,-(2 >> 1) = 255,即用白色表示边缘pBits2[y * bitmap2.bmWidthBytes + x * bpp2 + 0] = _gray;pBits2[y * bitmap2.bmWidthBytes + x * bpp2 + 1] = _gray;pBits2[y * bitmap2.bmWidthBytes + x * bpp2 + 2] = _gray;}}//------------------------------delete [] gray; gray = NULL;delete [] dx; dx = NULL;delete [] dy; dy = NULL;delete [] buffer; buffer = NULL;return 1;}/*-------------------Sobel图像二阶差分梯度边缘检测------------------------------ * 算法原理: * *   Sobel算子模板:https://en.wikipedia.org/wiki/Sobel_operator *            | -1  0  +1 |          | -1  -2  -1 | *       Sx = | -2  0  +2 |     Sy = |  0   0   0 | *            | -1  0  +1 |          | +1  +2  +1 | * * 函数名称: Canny(...) * 参数1: bitmap_src [in]输入的图像位图数据 * 参数2: bitmap_dst [out]输出的图像位图数据 * 参数3: low_thresh [in]低阈值,(暂时不使用该参数) * 参数4: high_thresh [in]高阈值,所有梯度幅值高于此值的点认为是边缘点 * * 过客 && 386520874@qq.com && 2015.03.28*/int CCannyEdgeDetection::Sobel(BITMAP &bitmap_src, BITMAP &bitmap_dst, double low_thresh, double high_thresh){//-------------1. 输入参数检查-----------------------BITMAP bitmap1 = bitmap_src;unsigned char* pBits1 = static_cast<unsigned char*>(bitmap1.bmBits);int bpp1 = bitmap1.bmBitsPixel/8; //bpp代表通道的数目,一般 bpp = 3int width2 = bitmap1.bmWidth;int height2 = bitmap1.bmHeight;CreateEmptyImage(bitmap_dst, width2, height2, bitmap1.bmBitsPixel);int bpp2 = bitmap_dst.bmBitsPixel/8; //bpp代表通道的数目,一般 bpp = 3unsigned char* pBits2 = static_cast<unsigned char*>(bitmap_dst.bmBits);BITMAP bitmap2 = bitmap_dst;//-------------2. RGB转灰度-----------------------int* gray = new int[width2 * height2]; //保存灰度图像数据for(int y = 0; y < bitmap1.bmHeight; y++){for(int x = 0; x < bitmap1.bmWidth; x++){int B = pBits1[y * bitmap1.bmWidthBytes + x * bpp1 + 0]; //Blueint G = pBits1[y * bitmap1.bmWidthBytes + x * bpp1 + 1]; //Greenint R = pBits1[y * bitmap1.bmWidthBytes + x * bpp1 + 2]; //Red//int A = R * 0.299 + G * 0.587 + B * 0.114; //一般RGB2Gray公式int A = R * 0.212671 + G * 0.715160 + B * 0.072169; //opencv的RGB2Gray公式 0.212671*R + 0.715160*G + 0.072169*Bgray[y * width2 + x] = A;}}//-------------3. 计算灰度图像梯度幅值和方向-----------------------//利用Sobel算子,计算x,y方向的偏导数for(int y = 0; y < height2; y++){for(int x = 0; x < width2; x++){if(x < 1 || x >= width2 -1 || y < 1 || y >= height2 - 1){continue;} //3x3的算子,图像的4条边需要跳过int dx = -(gray[(y - 1) * width2 + (x - 1)] * 1 + gray[(y + 0) * width2 + (x - 1)] * 2 + gray[(y + 1) * width2 + (x - 1)] * 1)+ (gray[(y - 1) * width2 + (x + 1)] * 1 + gray[(y + 0) * width2 + (x + 1)] * 2 + gray[(y + 1) * width2 + (x + 1)] * 1);int dy = -(gray[(y - 1) * width2 + (x - 1)] * 1 + gray[(y - 1) * width2 + (x + 0)] * 2 + gray[(y - 1) * width2 + (x + 1)] * 1)+ (gray[(y + 1) * width2 + (x - 1)] * 1 + gray[(y + 1) * width2 + (x + 0)] * 2 + gray[(y + 1) * width2 + (x + 1)] * 1);int Grad = abs(dx) + abs(dy); //计算梯度幅值if(Grad >= high_thresh){Grad = 255;}else{Grad = 0;}pBits2[y * bitmap2.bmWidthBytes + x * bpp2 + 0] = Grad;pBits2[y * bitmap2.bmWidthBytes + x * bpp2 + 1] = Grad;pBits2[y * bitmap2.bmWidthBytes + x * bpp2 + 2] = Grad;}}//计算梯度幅值和梯度的方向//.......delete [] gray; gray = NULL;return 1;}

/* * test.cpp * * 过客 && 386520874@qq.com && 2016.03.26 **/#include "CannyEdgeDetection.h"int main(int argc, char* argv[]) //专门测试字体的边缘轮廓检测{wchar_t in[260] = TEXT("./picture/test2.png");wchar_t out[260];CCannyEdgeDetection ced;BITMAP bitmap;HBITMAP hBitmap;BITMAP bitmap_dst;ced.OpenImage(in, bitmap, hBitmap);ced.Sobel(bitmap, bitmap_dst, 0, 400);ced.SaveImage(TEXT("./picture/test2.sobel.0_400.png"), bitmap_dst);ced.ReleaseBitmap(bitmap_dst);ced.Canny(bitmap, bitmap_dst, 10, 100);ced.SaveImage(TEXT("./picture/test2.canny.10_100.png"), bitmap_dst);ced.ReleaseBitmap(bitmap_dst);system("pause");return 1;}


------------------------------------------------测试示例1----------------------------------------------------------


                                                                                       图1.1 原始位图



                                     图1.2canny检测[low,high]=[10,100]



                                     图1.3 sobel检测[low,high]=[0,200]



                                     图1.4 sobel检测[low,high]=[0,400]


------------------------------------------------测试示例2----------------------------------------------------------

                              

                                     图2.1 原始位图


                              

                                 图2.2 canny检测[low,high]=[100,400]

                              
                                 图2.3 sobel检测[low,high]=[0,400]


--------------------------结论-------------------------------------

通过上面两个示例图片的比较,可以发现sobel的处理比较干净利落,

而canny算法的处理结果细节比较多一点,即线条的毛边比较多。

但canny的好处是它的线宽只有1个像素,而sobel则不一定。两者各

有利弊,在图像特征检测方面很难说谁好谁坏,不过听说卷积神经网络

的特征检测用了sobel算子。


0 0
原创粉丝点击