数字图像处理笔记2-nbsp;边沿检…
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); //下邻点
数字图像处理笔记2-nbsp;边沿检…相关推荐
- 数字图像处理笔记(一)——图像存储空间,分辨率,图像内插
数字图像处理笔记(一)--图像存储空间,分辨率,图像内插 本系列笔记是笔者在学习冈萨雷斯<数字图像处理>第三版时做的总结,日后看的时候方便点,如果有幸得到大家的讨论,喜上眉梢. 本节参考书 ...
- 数字图像处理笔记-02(图像空域增强技术及联合运用)
数字图像处理笔记-02(图像空域增强技术及联合运用) (一) 图像增强 1.1 基本概念 由于图像在传输或者处理过程中会引入噪声或使图像变模糊,从而降低了图像质量,甚至淹没了特 征,给分析带来了困难. ...
- 数字图像处理笔记(一)基础内容
基础内容 数字图像是什么,和模拟图像又有什么区别 数字图像处理 数字图像处理研究内容 数字图像处理有哪些方法 计算机图形学和数字图像处理的区别 常用的数字图像处理开发工具 数字图像 图像的数学表达 数 ...
- DIP数字图像处理笔记
数字图像处理--南信大 范春年老师 期末复习笔记 matlab语法 期中考试总结 邻域 图像增强 概念 直方图的图像增强 点处理 直方图均衡化histogram equalization 意义 效果 ...
- 【图像处理】数字图像处理笔记
文章目录 直方图处理 滤波器 图像复原 形态学图像处理 灰度形态学--多使用平坦结构元(SE)[数字图像处理P428 图像分割 1.canny边缘检测[数字图像处理P463] 图像的表征 特征描述子 ...
- 数字图像处理笔记一 - 图像采集(空间分辨率和幅度分辨率)
本文主要内容来自与<数字图像处理第二版中文版(冈萨雷斯)>第二章, 图像采集小节. 一.数字图像的表示 一幅图像可以被定义为一个二维函数f(x,y),其中(x,y)是空间(平面)坐标,在任 ...
- 数字图像处理笔记(一)空间分辨率与灰度分辨率
前言 因为在准备考研,复习专业课,复习的是<<数字图像处理>>,教材的话就是冈萨雷斯老师的<<数字图像处理>>第三版.把复习到的东西也就写一写,想一想, ...
- 数字图像处理笔记 第五章 图像增强 附实验
第五章 图像增强 附实验 5.1前言,基础概念与分类 图像增强的目的:改善图像的视觉效果,或将图像转换成更适合于人眼观察和机器分析识别的形式,以便从图像中获取更有用的信息 有针对性的,注意主观为导向, ...
- 数字图像处理笔记二 - 图片缩放(最近邻插值(Nearest Neighbor interpolation))
图片缩放的两种常见算法: 最近邻域内插法(Nearest Neighbor interpolation) 双向性内插法(bilinear interpolation) 本文主要讲述最近邻插值(Near ...
最新文章
- 电子学会 软件编程(图形化)二级训练营
- python encoding报错_菜鸟世界 -docker 环境下解决python 的 UnicodeEncodeError 错误
- 曼彻斯特解密_曼彻斯特编码解码方法与流程
- SAP Spartacus 电商云 UI Shipping Method 在单元测试环境下没有显示的问题
- navicat for mysql 数据库备份与还原
- 我是不会运行你的代码吗?不,我是不会导入自己的数据!
- C语言课程设计学生籍贯信息,C语言课程设计 学生籍贯信息记录簿设计.doc
- WINDOWS平台上扩展SGA,把你的内存用起来吧
- mybatis比mysql安全吗_MyBatis 和 SQL 注入的恩恩怨怨
- 编程之美---电梯调度算法
- MYSQL基础学习了解
- 计算机应用基础的课程讨论,(计算机教学论文:计算机应用基础课程教学方法的讨论.doc...
- 深度学习——人生为数不多的好出路
- Error: L6218E: Undefined symbol
- macOS Monterey 12.1 (21C52) 虚拟机 IOS 镜像
- 位 bit、字节 B Byte、M兆、MB
- 使用Gitee用于进行团队合作,(配合数据库迁移)
- 计算机课上玩的打字游戏,人教版信息技术三上第7课《玩打字游戏》教案.doc
- 前端萌新面试吃瘪经历分享
- Cassandra使用总结
热门文章
- JVM 中一次完整的 GC 流程是什么样子的,对象如何晋升到老年代,
- WannaCry病毒爆发并未对微软品牌造成太大影响
- MySQL的数据是存在哪的
- 内存检测之KFENCE
- MapKit 进阶教程: 自定义瓦片
- 亚洲领军汽车产业展会Automechanika Shanghai开幕丨Xtecher 前线
- kwgt 歌词_kwgt桌面插件美化下载-Eight for kwgt专业版主题包v3.9.136.1 最新版-腾飞网...
- RENESAS ISL15100IRZ-T7 单端口差分线路驱动器
- Eth-Trunk捆绑技术
- 输入一个整数将其倒着输出,如54321——12345。