0%

Opencv学习3

前言

Opencv学习-学习笔记3

cvCreateImage()函数

IplImage* cvCreateImage( CvSize size, int depth, int channels );

参数说明:
cvsize
size中的图像的宽度和高度。(宽度和高度的顺序需要注意)
depth
图像像素的位深度,值为可以为下面一种:
IPL_DEPTH_8U - 8位无符号整数
IPL_DEPTH_8S - 8位符号整数
IPL_DEPTH_16U - 16位无符号整数
IPL_DEPTH_16S - 16位符号整数
IPL_DEPTH_32S - 32位符号整数
IPL_DEPTH_32F - 单精度浮点数
IPL_DEPTH_64F - 双精度浮点数

channels
每个像素的通道数,可以为1,2,3或4。channels是交叉存储的,例如常用的数据布局方式为:b0 g0 r0 b1 g1 r1 …
尽管在一般IPL图像格式中可以以非交叉的方式存储,并且一些OpenCV可以处理它,但此函数只能创建交叉存储的图像。

形成:

1
2
3
cvCreateImage是下面两步操作的便捷形式:
header = cvCreateImageHeader(size,depth,channels);
cvCreateData(header);

例子:

1
2
3
4
如果我们要创建一个宽为360,高为640的3通道图像(RGB图像),可以采用如下语句:
IplImage* img=cvCreateImage( cvSize(360,640), IPL_DEPTH_8U,3 );
类似的,如果要初始化一张相同大小的灰度图像,可以采用如下语句:
IplImage* img=cvCreateImage( cvSize(360,640), IPL_DEPTH_8U,1 );

数据类型CvMemStorge&&CvSeq

链接网址

opencv中的内存存储结构CvMemStorage

  内存存储器是一个可用来存储诸如序列,轮廓,图形,子划分等动态增长数据结构的底层结构。它是由一系列以同等大小的内存块构成,呈列表型 。

1
2
3
4
5
6
7
8
typedef struct CvMemStorage
{
struct CvMemBlock* bottom;
struct CvMemBlock* top;
struct CvMemStorage* parent;
int block_size;
int free_space;
}CvMemStorage;

cvCreateMemStorage
__创建内存块__:
CvMemStorage* m_storage=cvCreateMemStorage(int block_size=0);
block_size:存储块的大小以字节表示。如果大小是 0 byte, 则将该块设置成默认值 当前默认大小为64k.
函数 cvCreateMemStorage 创建一内存块并返回指向块首的指针。起初,存储块是空的。
清空内存存储块
void cvClearMemStorage( CvMemStorage* m_storage );
函数 cvClearMemStorage 将存储块的 top 置到存储块的头部(注:清空存储块中的存储内容)。该函数并不释放内存(仅清空内存)。

可增长的元素序列CvSeq

CvSeq结构是所有OpenCV的一个基本的动态数据结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
typedef struct CvSeq
{
CV_SEQUENCE_FIELDS()
} CvSeq;

#define CV_SEQUENCE_FIELDS()
int flags; /* micsellaneous flags */
int header_size; /* 序列头的大小 */
struct CvSeq* h_prev; /* 前一个序列 */
struct CvSeq* h_next; /* 后一个序列 */
struct CvSeq* v_prev; /* 第二级前一个序列 */
struct CvSeq* v_next; /* 第二级后一个序列 */
int total; /* 元素的总个数 */
int elem_size;/* 元素的尺寸 */
char* block_max;/* 上一块的最大块 */
char* ptr; /* 当前写指针 */
int delta_elems; /*序列中快的大
(序列粒度) */
CvMemStorage* storage; /*序列的存储位置 */
CvSeqBlock* free_blocks; /* 未分配的块序列 */
CvSeqBlock* first; /* 指向第一个快序列 *

使用者用一种不常见的通过宏定义的帮助来实现结构的定义,使CvSeq结构可扩展,增加参数。用户可以自定义一个结构,然后通过宏定义CV_SEQUENCE_FIELDS()将自己定义的结构放在CvSeq参数后面组成一个新的结构。


二值化函数cvThreshold()

cvThreshold是opencv库中的一个函数。作用:函数 cvThreshold 对单通道数组应用固定阈值操作。该函数的典型应用是对灰度图像进行阈值操作得到二值图像。
阈值化函数
double cvThreshold(constCvArr* src, CvArr* dst, double threshold, double max_value,int threshold_type)

参数:
src –原始数组 (必须单通道灰度图, 8-bit of 32-bit 浮点数)
dst –输出数组 (必须单通道灰度图,与src的类型一致,或者为 8-bit)
threshold –阈值
max_value –使用 CV_THRESH_BINARY 和 CV_THRESH_BINARY_INV 的最大值
threshold_type –对图像取阈值的方法
取阈值方法的内容以及主要讲解在这个链接

:这里使用的阈值表示把大于这个阈值的内容提出,灰度图白色部分值高,也就是把白色部分提出,剩下的就是黑色的轮廓进行判断.


寻找轮廓cvFindContours()函数

使用这个函数在一个二值图中来寻找轮廓,用法很关键。本次实验中希望提取出外轮廓。

参考资料如下: OpenCV函数cvFindContours

函数声明:
int cvFindContours( CvArr* image, CvMemStorage* storage, CvSeq** first_contour,int header_size=sizeof(CvContour),int mode=CV_RETR_LIST,int method=CV_CHAIN_APPROX_SIMPLE, CvPoint offset=cvPoint(0,0) );

  • tour_buf 是需要查找轮廓的单通道灰度图像 ,
  • storage 是临时存储区 ,
  • contour是存储轮廓点的CvSeq实例,
  • CV_RECT_EXTERNAL 只查找外围轮廓,还有CV_RECT_TREE

输入图像image必须为一个2值单通道图像
contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示
hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i ][ 0 ] ~hierarchy[ i ][ 3 ],分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。
mode表示轮廓的检索模式

  • CV_RETR_EXTERNAL表示只检测外轮廓
  • CV_RETR_LIST检测的轮廓不建立等级关系
  • CV_RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
  • CV_RETR_TREE建立一个等级树结构的轮廓。具体参考contours.c这个demo

method为轮廓的近似办法

  • CV_CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
  • CV_CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
  • CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

识别轮廓方法

高斯过滤

简介

高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪过程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值经过加权平均后得到。高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中心像素点的值。

函数分析

void GaussianBlur(InputArray src,OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, intborderType=BORDER_DEFAULT )

  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。它可以是单独的任意通道数的图片,但需要注意,图片深度应该为CV_8U,CV_16U, CV_16S, CV_32F 以及 CV_64F之一。
  • 第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。比如可以用Mat::Clone,以源图片为模板,来初始化得到如假包换的目标图。
  • 第三个参数,Size类型的ksize高斯内核的大小。其中ksize.width和ksize.height可以不同,但他们都必须为正数和奇数。或者,它们可以是零的,它们都是由sigma计算而来。
  • 第四个参数,double类型的sigmaX,表示高斯核函数在X方向的的标准偏差。
  • 第五个参数,double类型的sigmaY,表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX,如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。
    为了结果的正确性着想,最好是把第三个参数Size,第四个参数sigmaX和第五个参数sigmaY全部指定到。
  • 第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。有默认值BORDER_DEFAULT,我们一般不去管它。

使用样例:
1canny算子检测.cpp中使用了高斯滤波
GaussianBlur(srcImage, srcImage, Size(3, 3), 0, 0, BORDER_DEFAULT);

注:使用IplImage*类型看这个教程

canny算子求边缘

教程链接:Canny算子,Sobel算子,Laplace算子,Scharr滤波器合辑

简介

Canny边缘检测算子是John F.Canny于 1986 年开发出来的一个多级边缘检测算法。更为重要的是 Canny 创立了边缘检测计算理论(Computational theory ofedge detection),解释了这项技术是如何工作的。Canny边缘检测算法以Canny的名字命名,被很多人推崇为当今最优的边缘检测的算法。
其中,Canny 的目标是找到一个最优的边缘检测算法,让我们看一下最优边缘检测的三个主要评价标准:
1.低错误率: 标识出尽可能多的实际边缘,同时尽可能的减少噪声产生的误报。
2.高定位性: 标识出的边缘要与图像中的实际边缘尽可能接近。
3.最小响应: 图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。
为了满足这些要求 Canny 使用了变分法,这是一种寻找满足特定功能的函数的方法。最优检测使用四个指数函数项的和表示,但是它非常近似于高斯函数的一阶导数。

函数详解

C++: void Canny(InputArray image,OutputArray edges, double threshold1, double threshold2, int apertureSize=3,bool L2gradient=false )

  • 第一个参数,InputArray类型的image,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位图像。
  • 第二个参数,OutputArray类型的edges,输出的边缘图,需要和源图片有一样的尺寸和类型。
  • 第三个参数,double类型的threshold1,第一个滞后性阈值。
  • 第四个参数,double类型的threshold2,第二个滞后性阈值。
  • 第五个参数,int类型的apertureSize,表示应用Sobel算子的孔径大小,其有默认值3。
  • 第六个参数,bool类型的L2gradient,一个计算图像梯度幅值的标识,有默认值false。

需要注意的是,这个函数阈值1和阈值2两者的小者用于边缘连接,而大者用来控制强边缘的初始段,推荐的高低阈值比在2:1到3:1之间。
调用示例:

1
2
3
4
//载入原始图   
Mat src = imread("1.jpg"); //工程目录下应该有一张名为1.jpg的素材图
Canny(src, src, 3, 9,3 );
imshow("【效果图】Canny边缘检测", src);
听说好看的人都关注了我的公众号《泫言》