边缘检测、Hough变换、轮廓提取、种子填充、轮廓跟踪
转自:http://blog.sina.com.cn/s/blog_6c083cdd0100nm4s.html
7.1 边沿检测
我们给出一个模板 和一幅图象 。不难发现原图中左边暗,右边亮,中间存在着一条明显的边界。进行模板操作后的结果如下: 。
可以看出,第3、4列比其他列的灰度值高很多,人眼观察时,就能发现一条很明显的亮边,其它区域都很暗,这样就起到了边沿检测的作用。
例如,一个梯度为45度方向模板 ,可以检测出135度方向的边沿。
在边沿检测中,常用的一种模板是Sobel 算子。Sobel 算子有两个,一个是检测水平边沿的 ;另一个是检测垂直平边沿的 。与 和 相比,Sobel算子对于象素的位置的影响做了加权,因此效果更好。
下面的几幅图中,图7.1为原图;图7.2为普通Sobel算子处理后的结果图;图7.3为各向同性Sobel算子处理后的结果图。可以看出Sobel算子确实把图象中的边沿提取了出来。
在程序中仍然要用到第3章介绍的通用3×3模板操作函数TemplateOperation,所做的操作只是增加几个常量标识及其对应的模板数组,这里就不再给出了。
常用的LOG算子是5×5的模板,如下所示 。到中心点的距离与位置加权系数的关系用曲线表示为图7.4。是不是很象一顶墨西哥草帽?所以,LOG又叫墨西哥草帽滤波器。
LOG的算法和普通模板操作的算法没什么不同,只不过把3×3改成了5×5,这里就不再给出了。读者可以参照第3章的源程序自己来完成。
7.2 Hough变换
Hough变化的原理是利用点和线的对偶性,将原始空间的给定的曲线通过曲线表达式变为参数空间的一个点。在原始图像坐标系下的一个点对应参数坐标系中的一条直线,同样参数坐标系的一条直线对应原始坐标中的一个点。原始坐标中呈现直线的所有点,它们的斜率和截距是相同的,所以他们在参数坐标下对应于同一个点。
首先,初始化一块缓冲徐,对应于参数平面,将所有的数据置0,对于图像的每一个前景点,求出参数平面对应的直线,把直线上所有的点都加1,最后找到参数平面最大的点的位置,这个位置就是原图像直线上的参数。
Hough变换用来在图象中查找直线。它的原理很简单:假设有一条与原点距离为s,方向角为θ的一条直线,如图7.6所示。
利用这个事实,我们可以找出某条直线来。下面将给出一段程序,用来找出图象中最长的直线(见图7.7)。找到直线的两个端点,在它们之间连一条红色的直线。为了看清效果,将结果描成粗线,如图7.8所示。
图7.7 原图 |
图7.8 Hough变换的结果 |
MYLINE *lpMyLine,*TempLine,MaxdLine;
static LOGPEN rlp={PS_SOLID,1,1,RGB(255,0,0)};
//我们处理的实际上是256级灰度图,不过只用到了0和255两种颜色。
MessageBox(hWnd,"Must be a mono bitmap with grayscale palette!",
"Error Message",MB_OK|MB_ICONEXCLAMATION);
Dist=(int)(sqrt((double)bi.biWidth*bi.biWidth+
(double)bi.biHeight*bi.biHeight)+0.5);
Alpha=180 /2 ; //0 到 to 178 度,步长为2度
if((hDistAlpha=GlobalAlloc(GHND,(DWORD)Dist*Alpha*
MessageBox(hWnd,"Error alloc memory!","Error Message",
if((hMyLine=GlobalAlloc(GHND,(DWORD)Dist*Alpha*
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
BufSize=OffBits+bi.biHeight*LineBytes;
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
lpDistAlpha=(int *)GlobalLock(hDistAlpha);
lpMyLine=(MYLINE *)GlobalLock(hMyLine);
for (i=0;i<(long)Dist*Alpha;i++){
TempLine=(MYLINE*)(lpMyLine+i);
(*TempLine).boty=32767; //初始化最低点的y坐标为一个很大的值
lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);
i=(long)fabs((x*cos(k*PI/180.0)+y*sin(k*PI/180.0)));
*(lpDistAlpha+i*Alpha+k/2)=*(lpDistAlpha+i*Alpha+k/2)+1;
TempLine=(MYLINE*)(lpMyLine+i*Alpha+k/2);
for (i=0;i<(long)Dist*Alpha;i++){
TempLine=(MYLINE*)(lpMyLine+i);
MaxdLine.topx=(*TempLine).topx;
MaxdLine.topy=(*TempLine).topy;
MaxdLine.botx=(*TempLine).botx;
MaxdLine.boty=(*TempLine).boty;
rhp = CreatePenIndirect(&rlp);
MoveToEx(hDc,MaxdLine.botx,MaxdLine.boty,NULL);
LineTo(hDc,MaxdLine.topx,MaxdLine.topy);
如果 是给定的,用上述方法,我们可以找到该方向上最长的直线。
其实Hough变换能够查找任意的曲线,只要你给定它的方程。这里,我们就不详述了。
7.3 轮廓提取
图7.9 原图 |
图7.10 轮廓提取 |
LPBITMAPINFOHEADER lpTempImgData;
//我们处理的实际上是256级灰度图,不过只用到了0和255两种颜色。
MessageBox(hWnd,"Must be a mono bitmap with grayscale palette!",
"Error Message",MB_OK|MB_ICONEXCLAMATION);
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
BufSize=OffBits+bi.biHeight*LineBytes;
if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
MessageBox(hWnd,"Error alloc memory!","Error Message",MB_OK|
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);
memcpy(lpTempImgData,lpImgData,BufSize);
for (y=1;y<bi.biHeight-1;y++){ //注意y的范围是从1到高度-2
lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);
lpTempPtr=(char *)lpTempImgData+(BufSize-LineBytes-y*LineBytes);
nw=(unsigned char)*(lpPtr+x+LineBytes-1);
n=(unsigned char)*(lpPtr+x+LineBytes);
ne=(unsigned char)*(lpPtr+x+LineBytes+1);
w=(unsigned char)*(lpPtr+x-1);
e=(unsigned char)*(lpPtr+x+1);
sw=(unsigned char)*(lpPtr+x-LineBytes-1);
s=(unsigned char)*(lpPtr+x-LineBytes);
se=(unsigned char)*(lpPtr+x-LineBytes+1);
*(lpTempPtr+x)=(unsigned char)255; //删除该黑点
hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData,
hf=_lcreat("c://outline.bmp",0);
_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));
_lwrite(hf,(LPSTR)lpTempImgData,BufSize);
7.4 种子填充
种子填充算法用来在封闭曲线形成的环中填充某中颜色,在这里我们只填充黑色。
这里,我们自己定义了一些堆栈的数据结构和操作,实现了堆栈的初始化、push、pop、判断是否为空、及析构。
BOOL InitStack(HWND hWnd,LONG StackLen)
SeedFillStack.ElementsNum=StackLen; //将堆栈的大小赋值
if((SeedFillStack.hMem=GlobalAlloc(GHND,SeedFillStack.ElementsNum*
MessageBox(hWnd,"Error alloc memory!","ErrorMessage",MB_OK|
SeedFillStack.lpMyStack=(POINT *)GlobalLock(SeedFillStack.hMem);
memset(SeedFillStack.lpMyStack,0,SeedFillStack.ElementsNum*
GlobalUnlock(SeedFillStack.hMem);
GlobalFree(SeedFillStack.hMem);
if(SeedFillStack.ptr>=SeedFillStack.ElementsNum)
TempPtr=(POINT *)(SeedFillStack.lpMyStack+SeedFillStack.ptr++);
return *(SeedFillStack.lpMyStack+SeedFillStack.ptr);
return (SeedFillStack.ptr==0)?TRUE:FALSE;
如果读者对堆栈的概念还不清楚,请参阅有关数据结构方面的书籍,这里就不详述了。
LPBITMAPINFOHEADER lpTempImgData;
//我们处理的实际上是256级灰度图,不过只用到了0和255两种颜色。
MessageBox(hWnd,"Must be a mono bitmap with grayscale palette!",
"Error Message",MB_OK|MB_ICONEXCLAMATION);
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
BufSize=OffBits+bi.biHeight*LineBytes;
if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
MessageBox(hWnd,"Error alloc memory!","Error Message",MB_OK|
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);
memcpy(lpTempImgData,lpImgData,BufSize);
if(!InitStack(hWnd,(LONG)bi.biHeight*bi.biWidth)){ //初始化堆栈
lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-SeedPoint.y*LineBytes)+SeedPoint.x;
//鼠标点到了黑点上,提示用户不能选择边界上的点,返回FALSE
MessageBox(hWnd,"The point you select is a contour point!",
"Error Message",MB_OK|MB_ICONEXCLAMATION);
//push该点(用户用鼠标选择的,处理是在WM_LBUTTONDOWN中
while(!IsStackEmpty()) //堆栈不空则一直处理
lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-CurP.y*LineBytes)+CurP.x;
if(*lpTempPtr1!=0) //如果为白,表示还没有填,进栈
lpTempPtr1=lpTempPtr+LineBytes;
if(*lpTempPtr1!=0) //如果为白,表示还没有填,进栈
if(CurP.x<bi.biWidth-1) //注意判断边界
if(*lpTempPtr1!=0) //如果为白,表示还没有填,进栈
if(CurP.y<bi.biHeight-1) //注意判断边界
lpTempPtr1=lpTempPtr-LineBytes;
if(*lpTempPtr1!=0) //如果为白,表示还没有填,进栈
hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData,
_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));
_lwrite(hf,(LPSTR)lpTempImgData,BufSize);
7.5 轮廓跟踪
轮廓跟踪,顾名思义就是通过顺序找出边缘点来跟踪出边界。图7.9经轮廓跟踪后得到的结果如图7.11所示。
LPBITMAPINFOHEADER lpTempImgData;
int direct[8][2]={{1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}};
//我们处理的实际上是256级灰度图,不过只用到了0和255两种颜色。
MessageBox(hWnd,"Must be a mono bitmap with grayscale palette!",
"Error Message",MB_OK|MB_ICONEXCLAMATION);
OffBits=bf.bfOffBits-sizeof(BITMAPFILEHEADER);
BufSize=OffBits+bi.biHeight*LineBytes;
if((hTempImgData=LocalAlloc(LHND,BufSize))==NULL)
MessageBox(hWnd,"Error alloc memory!","Error Message",
lpImgData=(LPBITMAPINFOHEADER)GlobalLock(hImgData);
lpTempImgData=(LPBITMAPINFOHEADER)LocalLock(hTempImgData);
memset(lpTempImgData,(BYTE)255,BufSize);
memcpy(lpTempImgData,lpImgData,OffBits);
for (y=0;y<bi.biHeight && !found; y++){
lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes);
for (x=0;x<bi.biWidth && !found; x++)
if (*(lpPtr++) ==0) found=TRUE;
//从循环退出时,x,y坐标都做了加1的操作。在这里把它们减1,得到
lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-StartP.y*LineBytes)+StartP.x;
*lpTempPtr=(unsigned char)0; //起始点涂黑
lpPtr=(char *)lpImgData+(BufSize-LineBytes-CurP.y*LineBytes)+CurP.x;
if(*lpPtr!=0){ //若右邻点为白,则找右下邻点
(BufSize-LineBytes-CurP.y*LineBytes)+CurP.x;
while (! ( (CurP.x==StartP.x) &&(CurP.y==StartP.y))){ //知道找到起始点,
lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-CurP.y*LineBytes)+CurP.x;
lpTempPtr=(char*)lpTempImgData+
(BufSize-LineBytes-y*LineBytes)+x;
lpPtr=(char *)lpImgData+(BufSize-LineBytes-y*LineBytes)+x;
if(((*lpPtr==0)&&(*lpTempPtr!=0))||
((x==StartP.x)&&(y==StartP.y)))
if(IsContourP(x,y,lpPtr)){ //若是个边界点
hBitmap=CreateDIBitmap(hDc,(LPBITMAPINFOHEADER)lpTempImgData,
hf=_lcreat("c://contour.bmp",0);
_lwrite(hf,(LPSTR)&bf,sizeof(BITMAPFILEHEADER));
_lwrite(hf,(LPSTR)lpTempImgData,BufSize);
//判断某点是不是边界点,参数x,y 为该点坐标,lpPtr为指向原数据的指针
BOOL IsContourP(LONG x,LONG y, char *lpPtr)
n=(unsigned char)*(lpPtr+LineBytes); //上邻点
w=(unsigned char)*(lpPtr-1); //左邻点
e=(unsigned char)*(lpPtr+1); //右邻点
s=(unsigned char)*(lpPtr-LineBytes); //下邻点
if(num==0) //全是黑点,说明是个内部点而不是边界点
边缘检测、Hough变换、轮廓提取、种子填充、轮廓跟踪相关推荐
- 边缘检测 Hough变换 轮廓提取 种子填充 轮廓跟踪
分享一下我老师大神的人工智能教程.零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow 转自:http:/ ...
- matlab 图像 轮廓 填充颜色,基于Matlab的图形轮廓提取及填充
计算机工程应用技术 本栏目责任编辑: 贾薇薇 电脑知识与技术 基于 Matlab 的图形轮廓提取及填充 井艾斌,柳青,孟祥增 (山东师范大学, 山东 济南 250014) 摘要: 提取图形的形状特征是 ...
- Udacity无人驾驶工程师课程笔记:1 计算机视觉基础——基于Hough变换的车道线提取
Udacity无人驾驶工程师课程笔记:1 计算机视觉基础--基于Hough变换的车道线提取 图像处理 颜色选择 区域遮罩 组合颜色和区域选择 边缘检测 Canny边缘检测 Hough变换 Hough变 ...
- hough变换连接边缘matlab,hough边缘检测matlab
利用matlab进行图像检测--直线提取_数学_自然科学_专业资料.Harbin Institute of Technology 图像工程导论 课程名称: ...(BW),'canny',thresh ...
- 【图像识别】基于 Hough变换钟表表盘识别Matlab代码
1 简介 本设计主要针对指针式仪表的数字化读数的研究,提高读取效率和读数的准确性.以MATLAB为载体对图像进行仿真处理,通过设备采集图片,对图像进行表盘定位,图像预处理,边缘检测,Hough变换等操 ...
- java+对图像进行直线检测_Java调用OpenCV进行Hough变换直线检测
private BitmapHoughTransFormLine(Bitmap bmp) { Mat rgbMat = new Mat(); //存储原图像的矩阵 Mat grayMat = new ...
- 边沿检测与提取,轮廓跟踪与Hough变换(转)
边沿检测与提取,轮廓跟踪 首先,引入了模板操作. 边沿检测 我们给出一个模板 和一幅图象 .不难发现原图中左边暗,右边亮,中间存在着一条明显的边界.进行模板操作后的结果如下: . 可以看出,第3.4列 ...
- hough变换连接边缘matlab,边缘检测与Hough变换实验报告 Matlab - 图文
<边缘检测与Hough变换实验报告 Matlab - 图文>由会员分享,可在线阅读,更多相关<边缘检测与Hough变换实验报告 Matlab - 图文(5页珍藏版)>请在人人文 ...
- 计算机视觉检测 白皓月,Hough变换和轮廓匹配相结合的瞳孔精确检测算法
摘要 针对红外眼部视频中瞳孔直径检测精度不够高的问题,提出了一种将Hough圆变换和轮廓匹配相结合的瞳孔检测算法(Hough-Contour).对每帧图像,首先进行灰度化并滤波去噪;然后提取边缘并利用 ...
最新文章
- linux|minicom使用方法汇总
- 《SQL Server企业级平台管理实践》读书笔记——几个系统库的备份与恢复
- python3 time
- 通过踩坑带你读透虚拟机的“锁粗化”
- mpi tcp连接报错_MPI分布式编程 --3.OpenMPI多节点运行报错
- MATLAB程序详细解析,遗传算法——matlab代码解析
- 如何获取客户端MAC地址(三个方法)
- 局域网的主机如何连接外网
- HBuilder与夜神模拟器
- Unity全新的版本发布计划(2018)【转自游戏蛮牛】
- python公历转农历_Python农历公历转换
- 直播服务器搭建NGINX-RTMP+JAVA
- 游戏设计---游戏中战斗力计算方法(整理)
- testerhome学习笔记2_Bash基础
- php摇号 中标 程序,摇号信息系统招标公告
- 关于class not fount的错误
- 苹果遵从荷兰命令 允许约会应用开发商提供第三方支付选项
- ubuntu查看网卡驱动
- java随机数代码解析,实例解析常用的java随机数生成办法
- Delving into Localization Errors for Monocular 3D Object Detection 论文学习
热门文章
- 【camera-radar】相机-毫米波雷达联合标定方案介绍+实现
- 度量学习:ArcFace算法和工程应用总结
- HDU - 5875 2016 ACM/ICPC 大连网络赛 H题 暴力
- OpenCV(基础补充)颜色空间HSV *args与**args(滑动条传参问题)
- 如何理解Nyquist采样定理?
- Go 分布式学习利器(9)-- Go语言 结构体的行为定义和实现
- Struts2 2.5版本新配置filter-class
- hive函数 get_json_object的使用
- [Linux] 010 权限管理命令 chmod
- 【刷题】BZOJ 4516 [Sdoi2016]生成魔咒