《图像处理技术》实验报告(最新三合一)

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

《图像处理技术》实验报告
姓名:李怀彬
工作单位:安徽省特殊教育中专学校
指导教师:童卫青
完成日期:2014年5月27日
1.实验1
1.1实验目的
了解和运用基于OpenCV的读、写和显示图像的方法
1.2实验所用的主要OpenCV函数的说明
1、cvLoadImage函数
函数原型:IplImage* cvLoadImage( const char* filename, int
flags=CV_LOAD_IMAGE_COLOR );
filename :要被读入的文件的文件名(包括后缀);
flags :指定读入图像的颜色和深度:
指定的颜色可以将输入的图片转为3通道(CV_LOAD_IMAGE_COLOR), 单通道(CV_LOAD_IMAGE_GRAYSCALE), 或者保持不变(CV_LOAD_IMAGE_ANYCOLOR)。

深度指定输入的图像是否转为每个颜色通道每象素8位,(OpenCV的早期版本一样),或者同输入的图像一样保持不变。

选中CV_LOAD_IMAGE_ANYDEPTH,则输入图像格式可以为8位无符号,16位无符号,32位有符号或者32位浮点型。

函数cvSaveImage保存图像到指定文件。

图像格式的的选择依赖于filename 的扩展名,请参考cvLoadImage。

只有8位单通道或者3通道(通道顺序为'BGR' )可以使用这个函数保存。

如果格式,深度或者通道不符合要求,请先用cvCvtScale 和cvCvtColor转换;或者使用通用的cvSave保存图像为XML或者YAML格式。

cvLoadImage函数使用方法
cvLoadImage( filename, -1 ); 默认读取图像的原通道数
cvLoadImage( filename, 0 ); 强制转化读取图像为灰度图
cvLoadImage( filename, 1 ); 读取彩色图
2、cvNamedWindow函数
简介:cvNamedWindow该函数为开放计算机视觉(OpenCV)库库函数,用来创建指定的窗口。

定义:cvNamedWindow该函数为开放计算机视觉(OpenCV)库库函数,用来创建指定的窗口。

参数:name 窗口的名字,它被用来区分不同的窗口,并被显示为窗口标题。

flags 窗口属性标志。

可以选择CV_WINDOW_AUTOSIZE(1)和0两种值。

CV_WINDOW_AUTOSIZE这个标志被设置后,如果用户不能手动改变窗口大小,窗口大小会自动调整以适合被显示图像(参考cvShowImage)。

0表示以固定的窗口尺寸显示图像。

函数cvNamedWindow创建一个可以放置图像和trackbar的窗口。

被创建的窗口可以通过它们的名字被引用。

如果已经存在这个名字的窗口,这个函数将不做任何事情。

3、cvShowImage函数
简介:该函数为开放计算机视觉(OpenCV)库库函数,用来在在指定窗口中显示图像。

定义:void cvShowImage( const char* name, const CvArr* image );
参数:name 窗口的名字。

image 被显示的图像。

函数cvShowImage 在指定窗口中显示图像。

如果窗口创建的时候被设定标志CV_WINDOW_AUTOSIZE,那么图像将以原始尺寸显示;否则,图像将被伸缩以适合窗口大小
4、cvSaveImage函数
简介
cvSaveImage
保存图像到文件
int cvSaveImage( const char* filename, const CvArr* image );
filename
文件名。

image
要保存的图像。

函数cvSaveImage保存图像到指定文件。

图像格式的的选择依赖于filename 的扩展名,请参考cvLoadImage。

只有8位单通道或者3通道(通道顺序为'BGR' )可以使用这个函数保存。

如果格式,深度或者通道不符合要求,请先用cvCvtScale 和cvCvtColor转换;或者使用通用的cvSave保存图像为XML或者YAML格式。

对单通道的图像进行保存,也有可能得到新的3通道图像。

例如:cvSaveImage("r.bmp",r);把程序中命名为r的单通道图像存储名字为r的bmp 格式下图像,得到新图像为3通道。

5、cvWaitKey函数
函数功能:
cvWaitKey()函数的功能是不断刷新图像,频率时间为delay,单位为ms。

返回值为当前键盘按键值。

所以显示图像时,如果需要在cvShowImage("xxxx.bmp",image)后加上while (cvWaitKey(n)==key)为大于等于0的数即可,那么程序将会停在显示函数处,不运行其他代码;直到键盘值为key的响应之后。

delay>0时,延迟"delay"ms,在显示视频时这个函数是有用的,用于设置在显示完一帧图像后程序等待"delay"ms再显示下一帧视频;如果使用
cvWaitKey(0)则只会显示第一帧视频。

返回值:如果delay>0,那么超过指定时间则返回-1;如果delay=0,将没有返回值。

如果程序想响应某个按键,可利用if(cvWaitKey(1)==Keyvalue);
经常程序里面出现if( cvWaitKey(10) >= 0 ) 是说10ms中按任意键进入此if块。

6、cvDestroyWindow函数
简介:cvDestroyWindow,该函数为开放计算机视觉(OpenCV)库库函数之一,用来销毁一个窗口。

定义:void cvDestroyWindow( const char* name );
参数:name 要被销毁的窗口的名字。

函数cvDestroyWindow销毁指定名字的窗口。

7、cvReleaseImage函数
cvReleaseImage(img)和free、delete相同,只是把该指针img所指的内存给释放掉,但并没有把img指针本身干掉,其地址仍然不变(非NULL),只是该地址对应的内存是垃圾,成了野指针。

如果此时不把img设置为NULL,会让人误以为img是个合法的指针。

在继续使用img之前,通常会用语句if (img != NULL)进行防错处理。

很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。

1.3本图像实验的主要步骤
第一步:设置环境变量
控制面板—用户账户-用户账户-更改我的环境变量-新建一个环境变量,变量名为Pash,变量值为“E:\DIP_TrainingCourse\lib”
第二步,实验代码执行
代码设计流程说明
定义文件头
#include<stdio.h>
#include"cv.h"
#include"highgui.h"
定义main函数
int main(void)
{
char img_i[1024] = "..\\img\\lena.jpg";//输º?入¨?图ª?像?的Ì?文?件t名?
char img_o[1024] = "exp1_out.jpg"; //输º?出?图ª?像?的Ì?文?件t名?
IplImage* pImg = NULL; //图ª?像?结¨¢构1体¬?的Ì?指?针?
读图像文件
pImg = cvLoadImage(img_i,CV_LOAD_IMAGE_COLOR);
if(pImg == NULL){
printf("图ª?像?文?件t不?存ä?在¨²\n");
return -1;
创建窗口
cvNamedWindow("Lena",CV_WINDOW_AUTOSIZE);
cvMoveWindow("Lena",0,0);
显示图像
cvShowImage("Lena",pImg);
写图像文件
cvSaveImage(img_o,pImg);
销毁窗口
cvDestroyWindow("Lena");
释放图像
cvReleaseImage(&pImg);
1.4实验结果和讨论
结果
通过本次实验,主要了解了基于OpenCV的读、写和显示图像的方法。

掌握了cvLoadImage、cvNamedWindow、cvShowImage、cvSaveImage、cvDestroyWindow、cvReleaseImage等函数的功能及使用方法。

为今后的学习奠定了基础。

讨论心得
本次实验,程序代码编译对环境变量要求较高,当环境变量改变时,影响程序结果的现实,即,程序只能通过编译,但无法显示结果。

存在的问题:
1、在本次实验过程中,由于对软件平台不够熟练,需要在老师或同学的帮
助下才能完成。

2、对程序中设计到的各种函数不太了解。

2.实验2
2.1实验目的
·了解和运用基于OpenCV的彩色图像转换为灰度图像的方法
·了解和运用基于OpenCV的图像旋转、缩放、平移和剪切的方法
2.2实验所用的主要OpenCV函数的说明
1、colorToGray
借助MFC实现彩图到灰度图的转换,包括图片显示、灰度转换和保存-With color pictures to grayscale MFC to achieve the conversion, including image display, grayscale conversion and preservation
2、AffineTransform
AffineTransform 类表示 2D 仿射变换,它执行从 2D 坐标到其他 2D 坐标的线性映射,保留了线的“直线性”和“平行性”。

可以使用一系列平移(translation)、缩放 (scale)、翻转 (flip)、旋转 (rotation) 和错切 (shear) 来构造仿射变换。

这样的坐标变换可以使用一个 3 行乘 3 列的矩阵来表示,最后一行默认为[ 0 0 1 ]。

此矩阵将源坐标 (x,y) 变换为目标坐标 (x',y'),这一过程将坐标视为列向量,并用矩阵乘以坐标向量,步骤如下:
[ x'] [ m00 m01 m02 ] [ x ] [ m00x + m01y + m02 ] [ y'] = [ m10 m11 m12 ] [ y ] = [ m10x + m11y + m12 ] [ 1 ] [ 0 0 1 ] [ 1 ] [ 1 ]
处理 90 度旋转在 AffineTransform 类的 rotate 方法的一些变量中,双
精度参数指定了以弧度表示的旋转角度。

这些方法为近似 90 度(包括倍数诸如180、270 和 360 度)的旋转提供特殊的处理,以便更有效地处理象限旋转这类常见情形。

这种特殊处理可以导致旋转角度非常接近于 90 度的倍数,从而可以将其视为正好是 90 度的倍数。

对于 90 度的小倍数,被视为象限旋转的角度范围大约是 0.00000121 度宽。

这一节解释为什么需要特别小心以及如何实现它。

因为 90 度用弧度表示是 PI/2,而 PI 是无限不循环小数(因此是无理数),所以它不能像用弧度表示的精确双精度值那样准确地表示 90 度的倍数。

因此,理论上不可以使用 PI 来描述象限旋转(90、180、270 或 360 度)。

双精度浮点值可以非常接近 PI/2 的非零倍数,但是不够接近到正弦或余弦能精确到
0.0、1.0 或 -1.0。

只有在 Math.sin(0.0) 的情况下,Math.sin() 和 Math.cos() 实现才相应地返回 0.0。

但是,对于接近每个 90 度倍数的某些范围内的数,相样的实现却返回 1.0 和 -1.0,因为正确答案是如此接近 1.0 或 -1.0,以至于双精度的有效位数无法像表示接近 0.0 的数那样精确地表示差值。

这些问题归结为:如果在执行这些基于弧度的旋转操作期间,使用
Math.sin() 和 Math.cos() 方法直接生成用于矩阵修改的值,那么严格来说,得到的变换不能归类为象限旋转(即使对于 rotate(Math.PI/2.0) 这样的简单情况也是如此),因为执行正弦和余弦操作而得到的非 0.0 值将造成矩阵的细微变化。

如果这些变换不能归类为象限旋转,那么试图根据变换类型优化下一步操作的后续代码会被移交到它最通用的实现中。

因为象限旋转相当常见,所以在对变换应用旋转和对坐标应用所得变换时,此类应该快速合理地处理这些情况。

为了达到最佳处理,以弧度为单位测量旋转角度的方法试图检测象限旋转的角度并以这种方式处理它们。

因此,如果
Math.sin(theta) 或 Math.cos(theta) 正好返回 1.0 或 -1.0,那么这些方法会将角度theta视为象限旋转。

实际经验证明,此特性可用于 Math.PI/2.0 的小倍数大约 0.0000000211 弧度(0.00000121 度)的范围。

3、cvCvtColor
cvCvtColor(...),是Opencv里的颜色空间转换函数,可以实现RGB颜色向HSV,HSI等颜色空间的转换,也可以转换为灰度图像。

参数CV_RGB2GRAY是RGB到gray,
参数 CV_GRAY2RGB是gray到RGB.处理结果是彩色的,则转灰色就是了:
void cvCvtColor( const CvArr* src, CvArr* dst, int code );
src
输入的 8-bit,16-bit或 32-bit单倍精度浮点数影像。

dst
输出的8-bit, 16-bit或 32-bit单倍精度浮点数影像。

code
色彩空间转换的模式,该code来实现不同类型的颜色空间转换。

比如
CV_BGR2GRAY表示转换为灰度图,CV_BGR2HSV将图片从RGB空间转换为HSV空间。

其中当code选用CV_BGR2GRAY时,dst需要是单通道图片。

当code选用
CV_BGR2HSV时,对于8位图,需要将RGB值归一化到0-1之间。

这样得到HSV
图中的H范围才是0-360,S和V的范围是0-1。

这里给出将RGB图像转换为灰度和HSV的例子:
IplImage *src = cvLoadImage("4085_1.jpg",1);//原图
IplImage *dst_gray = cvCreateImage(cvGetSize(src),src->depth,1);//灰度图
IplImage *dst_image = cvCreateImage(cvGetSize(src),32,src->nChannels); IplImage *src_image_32 =
cvCreateImage(cvGetSize(src),32,src->nChannels);
//这两个图需要是32浮点位的,因为对原图进行归一化后得到的是浮点数cvCvtColor(src,dst_gray,CV_BGR2GRAY);//得到灰度图
cvConvertScale(src,src_image_32,1.0/255.0,0);//将原图RGB归一化到0-1之间
cvCvtColor(src_image_32,dst_image,CV_BGR2HSV);//得到HSV图
4、cvMat
OpenCV中重要的矩阵变换函数,使用方法为CvMat* cvCreateMat ( int rows, int cols, int type ); 这里type可以是任何预定义类型,预定义类型的结构如下:CV_<bit_depth> (S|U|F)C<number_of_channels>。

于是,矩阵的元素可以是32位浮点型数据(CV_32FC1),或者是无符号的 8位三元组的整型数据(CV_8UC3),或者是无数的其他类型的元素。

一个CvMat的元素不一定就是个单一的数字。

在矩阵中可以通过单一(简单)的输入来表示多值,这样我们可以在一个三原色图像上描绘多重色彩通道。

对于一个包含RGB通道的简单图像,大多数的图像操作将分别应用于每一个通道(除非另有说明)。

CvMat 结构:
typedef struct CvMat
{
int type;
int step;
int* refcount;/* for internal use only */
int hdr_refcount;
union
{
uchar* ptr;
short* s;
int* i;
float* fl;
double* db;
} data;
union
{
int rows;
int height;
};
union
{
int cols;
int width;
};
} CvMat;
矩阵由宽度(width),高度(height),
类型(type),行数据长度(step,行的长度用字节表示而不是用整形或者浮点型长度)和一个指向数据的指针构成
2.3本图像实验的主要步骤
第一步:程序代码
#include <stdio.h>
#include "cv.h"
#include "highgui.h"
// 彩色图像转换为灰度图像
int colorToGray(void);
// affine变换
int affineTrans(float m[6]); int main(void)
{
int angle; //旋转角度(单位度,顺时针为正)
float phi; //旋转角度(单位弧度,顺时针为正)
CvPoint cen;//图像旋转中心
float m[6]; //affine变化矩阵
// 1.彩色图像转换为灰度图像
printf("1.彩色图像转换为灰度图像\n");
colorToGray();
// affine变换
// 2.图像平移变换
m[0] = 1; m[1] = 0; m[2] = 10;
m[3] = 0; m[4] = 1; m[5] = 10;
printf("2.图像平移变换
\n");
affineTrans(m);
// 3.图像缩小变换
m[0] = 0.5F; m[1] = 0;
m[2] = 0;
m[3] = 0; m[4] = 0.5F;
m[5] = 0;
printf("3.图像缩小变换
\n");
affineTrans(m);
// 4.图像放大变换
m[0] = 1.5F; m[1] = 0;
m[2] = 0;
m[3] = 0; m[4] = 1.5F;
m[5] = 0;
printf("4.图像放大变换
\n");
affineTrans(m);
// 5.图像旋转变换
angle = 30;
cen.x = 256;
cen.y = 256;
phi =
(float)(angle*CV_PI/180);
m[0] = (float)cos(phi); m[1]
= -(float)sin(phi); m[2] =
(float)( cen.x - cen.x * cos(phi)
+ cen.y * sin(phi) );
m[3] = (float)sin(phi); m[4]
= (float)cos(phi); m[5] =
(float)( cen.y - cen.x * sin(phi)
- cen.y * cos(phi) );
printf("5.图像旋转变换
\n");
affineTrans(m);
// 6.图像剪切变换
m[0] = 1; m[1] = 0.3F; m[2] = 0;
m[3] = 0.3F; m[4] = 1; m[5] = 0;
printf("6.图像剪切变换
\n");
affineTrans(m);
return 0;
}
// 彩色图像转换为灰度图像
int colorToGray(void)
{
char img_i[1024] =
"..\\img\\lena.jpg";//输入图像文件名
char img_o[1024] =
"exp2_out1.jpg"; //输出图像文件名
IplImage* pImg1 = NULL;
IplImage* pImg2 = NULL;
//读图像文件
pImg1 = cvLoadImage( img_i, CV_LOAD_IMAGE_COLOR );
if(pImg1 == NULL){
printf("图像文件不存
在\n");
return -1;
}
//创建窗口
cvNamedWindow("src",
CV_WINDOW_AUTOSIZE);
cvMoveWindow("src",0,0);
cvNamedWindow("dst",
CV_WINDOW_AUTOSIZE);
cvMoveWindow("dst",525,0);
//分配图像内存
pImg2 =
cvCreateImage(cvSize(pImg1->wi
dth,pImg1->height),IPL_DEPTH_8
U,1);
//把彩色图像转换为灰度图像
cvCvtColor(pImg1,pImg2,CV_
BGR2GRAY);
//显示图像
cvShowImage("src",pImg1);
cvShowImage("dst",pImg2);
//写图像文件
cvSaveImage( img_o, pImg2);
//等待按键
cvWaitKey(0);
//销毁窗口
cvDestroyWindow("src");
cvDestroyWindow("dst");
//释放图像
cvReleaseImage(&pImg1);
cvReleaseImage(&pImg2);
return 0;
}
// affine变换
int affineTrans(float m[6])
{
CvMat M;// 2×3变换矩阵
char img_i[1024] =
"..\\img\\lena.jpg";//输入图像
文件名
char img_o[1024] =
"exp2_out2.jpg"; //输出图像
文件名
IplImage* src = NULL;
IplImage* dst = NULL;
//读图像文件
src =
cvLoadImage(img_i,CV_LOAD_IMAG
E_COLOR);
if(src == NULL){
printf("图像文件不存
在\n");
return -1;
}
//创建窗口
cvNamedWindow("src",
CV_WINDOW_AUTOSIZE);
cvMoveWindow("src",0,0);
cvNamedWindow("dst",CV_WIN
DOW_AUTOSIZE);
cvMoveWindow("dst",src->wi dth + 10,0);
//分配图像内存
dst =
cvCreateImage(cvSize(src->widt h,src->height),src->depth,src->nChannels);
// 创建affine变换矩阵(2×3)
M = cvMat(2,3,CV_32F,m); // 进行affine变换 dst(x,y)
= M * src(x,y)
cvWarpAffine(src,dst,&M,CV
_INTER_LINEAR +
CV_WARP_FILL_OUTLIERS,cvScalar
All(0));
//显示图像
cvShowImage("src",src);
cvShowImage("dst",dst);
//保存图像
cvSaveImage( img_o,dst);
//等待按键
cvWaitKey(0);
//销毁窗口
cvDestroyWindow("src");
cvDestroyWindow("dst");
//释放图像
cvReleaseImage(&src);
cvReleaseImage(&dst);
return 0;
}
第二步:编译程序
在visual studio编辑器窗口执行Build——Build exp2结果显示编译成功,如下图所示:
第三步:显示运行结果
执行:按F5执行程序
1.4实验结果和讨论
结果
1、将彩色图像转换为灰度图像
2、对图像进行了旋转、缩放、平移和剪切操作。

旋转:围绕图像中心进行旋转
缩放:对图像进行放大或缩小操作
平移:图像在平面坐标内进行横向和纵向移动
剪切:对图像按预定算法进行裁剪。

存在的问题:
1、开始程序编译成功,但无法显示结果。

3.实验3 3.1实验目的
·了解和运用基于OpenCV的直方图均衡化的方法
·了解和运用基于OpenCV的边缘检测的方法
3.2实验所用的主要OpenCV函数的说明
1、equalizeHist()
该函数为开放计算机视觉(OpenCV)库库函数,用来使灰度图象直方图均衡化。

定义:void cvEqualizeHist( const CvArr* src, CvArr* dst );
参数:
src输入的8-比特单信道图像
dst输出的图像与输入图像大小与数据类型相同
函数cvEqualizeHist 采用如下法则对输入图像进行直方图均衡化:
1:计算输入图像的直方图H
2:直方图归一化,因此直方块和为255
3:计算直方图积分
4:采用H'作为查询表:dst(x,y)=H'(src(x,y))进行图像变换。

该方法归一化图像亮度和增强对比度。

直方图均衡化,可以将比较淡的图像变换为比较深的图像(即增强图像的亮度及对比度)。

直方图均衡化后面潜在的数学原理是一个分布(输入的亮度直方图)被映射到另一个分布(一个更宽,理想统一的亮度值分布),映射函数是一个累积分布函数。

对于连续分布,结果将是准确的均衡化。

在cvEqualizeHist中,原始图像及目标图像必须是单通道,大小相同的8位图像,对于彩色图像,必须先将每个通道分开,再分别进行直方图均衡化处理,然后将通道合并形成新的图像。

2、sobel()
使用扩展Sobel 算子计算一阶、二阶、三阶或混合图像差分
void cvSobel( const CvArr* src, CvArr* dst, int xorder, int yorder, int aperture_size=3 );
src
输入图像.
dst
输出图像.
xorder
x 方向上的差分阶数
yorder
y 方向上的差分阶数
aperture_size
扩展Sobel 核的大小,必须是1, 3, 5 或7。

除了尺寸为1,其它情况下,aperture_size ×aperture_size 可分离内核将用来计算差分。

对aperture_size=1的情况,使用3x1 或1x3 内核(不进行高斯平滑操作)。

这里有一个特殊变量CV_SCHARR (=-1),对应3x3 Scharr 滤波器,可以给出比3x3 Sobel 滤波更精确的结果。

对x-方向或矩阵转置后对y-方向。

函数cvSobel 通过对图像用相应的内核进行卷积操作来计算图像差分
由于Sobel 算子结合了Gaussian 平滑和微分,所以,其结果或多或少对噪声有一定的鲁棒性。

通常情况,函数调用采用如下参数(xorder=1, yorder=0, aperture_size=3) 或(xorder=0, yorder=1, aperture_size=3) 来计算一阶x- 或y- 方向的图像差分。

由于该函数不进行图像尺度变换,所以和输入图像(数组)相比,输出图像(数组)的元素通常具有更大的绝对数值(译者注:即像素的位深)。

为防止溢出,当输入图像是8 位的,要求输出图像是16 位的。

当然可以用函数cvConvertScale 或cvConvertScaleAbs 把运算结果(dst)转换为8 位的。

除了8-位图像,函数也接受32-位浮点数图像。

所有输入和输出图像都必须是单通道的,并且具有相同的图像尺寸或者ROI尺寸。

3、laplace()
即拉普拉斯算子,拉普拉斯算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度(▽f)的散度(▽·f)。

定义
拉普拉斯算子是n维欧几里德空间中的一个二阶微分算子,定义为梯度(▽f)的散度(▽·f)。

因此如果f是二阶可微的实函数,则f的拉普拉斯算子定义为:
f的拉普拉斯算子也是笛卡儿坐标系xi中的所有非混合二阶偏导数:
作为一个二阶微分算子,拉普拉斯算子把C函数映射到C函数,对于k≥ 2。

表达式(1)(或(2))定义了一个算子Δ :C(R) →C(R),或更一般地,定义了一个算子Δ :C(Ω) →C(Ω),对于任何开集Ω。

函数的拉普拉斯算子也是该函数的黑塞矩阵的迹

2表示式
二维空间:其中x与y代表x-y 平面上的笛卡儿坐标:
另外极坐标的表示法为:
三维空间
笛卡儿坐标系下的表示法
圆柱坐标系下的表示法
球坐标系下的表示法
N 维空间
在参数方程为(其中以及)的N维球坐标系中,拉普拉斯算子为:
其中是N− 1维球面上的拉普拉斯-贝尔特拉米算子。

4、canny()
Canny边缘检测算子是John F. Canny于1986 年开发出来的一个多级边缘检测算法。

是John F. Canny于1986 年开发出来的一个多级边缘检测算法。

更为重要的是Canny 创立了边缘检测计算理论(Computational theory of edge detection)解释这项技术如何工作。

Canny 算法的发展
Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:(1)最优检测:算法能够尽可能多地标识出图像中的实际边缘,漏检真实边缘的概率和误检非边缘的概率都尽可能小;(2)最优定位准则:检测到的边缘点的位置距离实际边缘点的位置最近,或者是由
于噪声影响引起检测出的边缘偏离物体的真实边缘的程度最小;(3)检测点与边缘点一一对应:算子检测的边缘点与实际边缘点应该是一一对应。

为了满足这些要求Canny 使用了变分法,这是一种寻找满足特定功能的函数的方法。

最优检测使用四个指数函数项表示,但是它非常近似于高斯函数的一阶导数。

Canny 算法的步骤
降噪
任何边缘检测算法都不可能在未经处理的原始数据上很好地工作,所以第一步是对原始数据与高斯mask 作卷积,得到的图像与原始图像相比有些轻微的模糊(blurred)。

这样,单独的一个像素噪声在经过高斯平滑的图像上变得几乎没有影响。

寻找图像中的亮度梯度
图像中的边缘可能会指向不同的方向,所以Canny 算法使用 4 个mask 检测水平、垂直以及对角线方向的边缘。

原始图像与每个mask 所作的卷积都存储起来。

对于每个点我们都标识在这个点上的最大值以及生成的边缘的方向。

这样我们就从原始图像生成了图像中每个点亮度梯度图以及亮度梯度的方向。

在图像中跟踪边缘
较高的亮度梯度比较有可能是边缘,但是没有一个确切的值来限定多大的亮度梯度是边缘多大又不是,所以Canny 使用了滞后阈值。

滞后阈值需要两个阈值——高阈值与低阈值。

假设图像中的重要边缘都是连续的曲线,这样我们就可以跟踪给定曲线中模糊的部分,并且避免将没有组成曲线的噪声像素当成边缘。

所以我们从一个较大的阈值开始,这将标识出我们比较确信的真实边缘,使用前面导出的方向信息,我们从这些真正的边缘开始在图像中跟踪整个的边缘。

在跟踪的时候,我们使用一个较小的阈值,这样就可以跟踪曲线的模糊部分直到我们回到起点。

柑橘canny边缘提取
一旦这个过程完成,我们就得到了一个二值图像,每点表示是否是一个边缘点。

一个获得亚像素精度边缘的改进实现是在梯度方向检测二阶方向导数的过零点它在梯度方向的三阶方向导数满足符号条件
其中Lx, Ly ... Lyyy 表示用高斯核平滑原始图像得到的尺度空间表示L计算得到的偏导数。

用这种方法得到的边缘片断是连续曲线,这样就不需要另外的边缘跟踪改进。

滞后阈值也可以用于亚像素边缘检测。

参数
Canny 算法包含许多可以调整的参数,它们将影响到算法的计算的时间与实效。

高斯滤波器的大小:第一步所有的平滑滤波器将会直接影响Canny 算法的结果。

较小的滤波器产生的模糊效果也较少,这样就可以检测较小、变化明显的细线。

较大的滤波器产生的模糊效果也较多,将较大的一块图像区域涂成一个特定点的颜色值。

这样带来的结果就是对于检测较大、平滑的边缘更加有用,例如彩虹的边缘。

阈值:使用两个阈值比使用一个阈值更加灵活,但是它还是有阈值存在的共性问题。

设置的阈值过高,可能会漏掉重要信息;阈值过低,将会把枝节信息看得很重要。

很难给出一个适用于所有图像的通用阈值。

目前还没有一个经过验证的实现方法。

OpenCV中的Canny函数
采用Canny 算法做边缘检测
void cvCanny( const CvArr* image, CvArr* edges, double threshold1,double threshold2, int aperture_size=3 );
--image 输入图像.
--edges 输出的边缘图像
--threshold1 第一个阈值
--threshold2 第二个阈值
--aperture_size Sobel 算子内核大小(见cvSobel).
函数cvCanny 采用CANNY 算法发现输入图像的边缘而且在输出图像中标识这些边缘。

threshold1和threshold2 当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。

3.3本图像实验的主要步骤
第一步、关键代码实现
1、定义主函数:
int main(void)
{
// 1.直方图均衡化处理
printf("1.直方图均衡化处理\n");
equalizeHist();
// 2.sobel边缘检测
printf("2.sobel边缘检测\n");
sobel();
// place边缘检测
printf("place边缘检测\n");
laplace();
// 4.Canny边缘检测
printf("4.Canny边缘检测\n");
canny();
return 0;
}
2、灰度图像的直方图均衡化处理
int equalizeHist(void)
{
char img_i[1024] = "..\\img\\rice.png";//输入图像文件名
char img_o[1024] = "exp3_EQ.jpg"; //输出图像文件名
IplImage* src = NULL;
IplImage* dst = NULL;
3、Sobel边缘检测
int sobel(void)
{
char img_i[1024] = "..\\img\\rice.png";//输入图像文件名
char img_o[1024] = "exp3_sobel.jpg"; //输出图像文件名
IplImage* src = NULL;
IplImage* dst = NULL;
IplImage* edge = NULL;
double maxVal,minVal;
double scale,shift;
4、 Laplace边缘检测
int laplace(void)
{
char img_i[1024] = "..\\img\\rice.png";//输入图像文件名
c har img_o[1024] = "exp3_laplace.jpg"; //输出图像文件名 IplImage* src = NULL;
I plImage* dst = NULL;
I plImage* edge = NULL;
d oubl
e maxVal,minVal;
d oubl
e scale,shift;
5、 Canny边缘检测
int canny(void)
{
d oubl
e th;
char img_i[1024] = "..\\img\\rice.png"; //输入图像文件名
c har img_o[1024] = "exp3_canny.jpg"; //输出图像文件名 IplImage* src = NULL;
I plImage* dst = NULL;
第二步:编译程序
在visual studio编辑器窗口执行Build——Build exp2结果显示编译成功,如下图所示:
第三步:显示运行结果
执行:按F5执行程序
3.4实验结果和讨论
实验结果
1、对图像进行直方图均衡化处理,输出结果如下图
2、对图像进行sobel边缘检测,输出结果如下图:
3、对图像进行Laplace边缘检测,输出结果如下图
4、对图像进行Canny边缘检测,输出结果如下图。

相关文档
最新文档