Opencv /c/c++ 请帮忙解释如下c语言代码大全表解释含义

豆丁微信公众号
君,已阅读到文档的结尾了呢~~
基于OpenCV的裂纹纹检测系统C++源代码,检测图像中是否存在裂纹并标识。
扫扫二维码,随身浏览文档
手机或平板扫扫即可继续访问
基于OpenCV的裂纹纹检测系统C++源代码
举报该文档为侵权文档。
举报该文档含有违规或不良信息。
反馈该文档无法正常浏览。
举报该文档为重复文档。
推荐理由:
将文档分享至:
分享完整地址
文档地址:
粘贴到BBS或博客
flash地址:
支持嵌入FLASH地址的网站使用
html代码:
&embed src='http://www.docin.com/DocinViewer--144.swf' width='100%' height='600' type=application/x-shockwave-flash ALLOWFULLSCREEN='true' ALLOWSCRIPTACCESS='always'&&/embed&
450px*300px480px*400px650px*490px
支持嵌入HTML代码的网站使用
您的内容已经提交成功
您所提交的内容需要审核后才能发布,请您等待!
3秒自动关闭窗口在Mac下使用OpenCV, 在Xcode下使用OpenCV (非常基础,详细)
已有 37294 次阅读
|个人分类:|系统分类:|关键词:OpenCV,Mac,XCode,Eclipse,图像,使用,配置|
&&& 笔者属于Mac小白,最近刚入手一台macbook air,一直没有时间搭建编程环境,这两天终于挤出时间开始使用Mac系统。目前的编程工具主要为eclipse以及xcode,笔者首先选择了使用xcode,由于本人长期使用C++从事图像处理方面的工作,所以安装opencv就成了编程开始前的第一步。系统环境:ML10.8.2XCode版本:4.5.2&OpenCV版本:2.4.3Eclipse版本:Juno Service Release 1&在这里特别提醒大家:&&& opencv中文官方网站介绍的“opencv在Mac OS系统下的安装方法”已经严重过期,我已经试过了,失败,而且这个方法不是opencv for mac的,而是opencv for IOS的,希望大家不要再走弯路尝试了。&
& & 为了方便刚刚接触mac的朋友,我在这里首先说明下opencv在mac下的形式。主要有两种:1. OpenCV for Mac& 2. OpenCV
for IOS 其中IOS的版本是为了让你的opencv使用在iphone或者ipad项目中的,即是给编写跑在IOS系统中的程序用的。而MAC的版本是给编写跑在MAC系统中的程序用的,这点非常重要,大家一定要根据自己的需求,而下载对应的版本。笔者就是因为开始弄不清楚这两个版本的区别而走了很多的弯路,现在和大家分享,希望有所帮助。
下面附上opencv官方网站的两个版本的下载链接:
目前的版本为2.4.3
好了,下面进入正题,笔者首先介绍OpenCV for Mac,即编写在mac系统上可以运行的C++程序。
一、安装OpenCV for MAC
首先下载opencv for mac安装源文件,解压缩
安装cmake程序。笔者使用的是Homebrew,在终端中输入:“brew install cmake”,自动安装cmake。
进入存放解压后的opencv文件夹,新建一个空的文件夹release,进入该文件夹,编译安装opencv,使用命令如下:
& & & &mkdir release
& & & &cd release
& & & &cmake -G "Unix Makefiles" ..
& & & &make
& & & &sudo make install
4. & & 安装好的lib文件存放在“/usr/local/lib”文件夹,h文件存放在“/usr/local/include”。
& & & &至此,opencv for Mac 安装完毕,参考的网址如下:
二、在MAC下使用OpenCV
1.&新建DisplayImage.cpp文件,代码如下
&highgui.h&
int main( int argc, char** argv )
& image = imread( argv[1], 1 );
& if( argc != 2 || !image.data )
&&&&& printf( "No
image data n" );
&&&&& return -1;
& namedWindow( "Display
Image", CV_WINDOW_AUTOSIZE );
& imshow( "Display
Image", image );
& waitKey(0);
& return 0;
2.&新建CMakeLists.txt文件,代码如下:
& &project( DisplayImage )
&& find_package( OpenCV REQUIRED )
&& add_executable( DisplayImage DisplayImage )
& &target_link_libraries( DisplayImage ${OpenCV_LIBS} )
3.&编译两个文件
&DisplayImage_directory&
&& cmake .
4.&运行编译好的结果
./DisplayImage lena.jpg
三、在XCode中使用OpenCV
1. 创建一个空的command line工程。
2. 在main.cpp中粘贴以下代码:
//& main.cpp
FbyCharacterNormalization
//& Created by
Boyuan Feng on 13-1-24.
//& Copyright (c)
2013年 Boyuan Feng. All rights reserved.
#include &iostream&
#include &opencv2/opencv.hpp&
#include &opencv2/highgui/highgui.hpp&
#include &opencv/cvaux.hpp&
#include &fstream&
#define BYTE unsigned char
int main(int argc, const char *
&&& // insert code here...
&&& //get the image from the directed path
&&&&&&&&&& IplImage* img = cvLoadImage("/Users/boyuanfeng/aaa.bmp", 1);
&&&&&&&&&& //NSLog(img);
&&&&&&&&&& //create a window to display the image
&&&&&&&&&& cvNamedWindow("picture", 1);
&&&&&&&&&& //show the image in the window
&&&&&&&&&& cvShowImage("picture", img);
&&&&&&&&&& //wait for the user to hit a key
&&&&&&&&&& cvWaitKey(0);
&&&&&&&&&& //delete the image and window
&&&&&&&&&& cvReleaseImage(&img);
&&&&&&&&&& cvDestroyWindow("picture");
&&&&&&&&&& //return
&&&&&&&&&& return 0;
3. 添加lib文件:右键点击工程名,选择“Add files to..”,在文件选择对话框弹出来时输入“/”,在弹出的路径框中输入:/usr/local/lib,全选该文件夹下的全部dylib文件,添加至工程。
5. 添加lib文件查找支持: 点击工程名文件,进入“Build Settings”选项卡,在“Library Search Paths”栏中输入“/usr/local/lib”
6. 添加头文件:点击工程名文件,进入“Build Settings”选项卡,在“Header Search Paths”栏中输入:“/usr/local/include& &/usr/local/include/opencv”
7. 编译运行整个工程,运行成功~~四、在Eclipse中使用OpenCV1. 按照正常的步骤,使用eclipse建立一个Mac C++工程,包含一个cpp文件,代码如xcode中的代码相同即可。2. 右击工程名, 选择“Properties”,在属性配置页中选择,点击C/C++ Build, 在下拉选项中选择&Settings. 在右边的选项卡中选择&Tool Settings。3. 在GCC C++ Compiler选项列表中选择Includes,在Include paths(-l)中添加安装好的opencv的头文件存放目录:/usr/local/include/4.&在MacOS X C++Linker选项列表中选择Library,在Library search path (-L)中添加安装好的opencv Lib文件存放目录:/usr/local/lib/5.&在MacOS X C++Linker选项列表中选择Library, 在Libraries(-l)&中依次点击“+”号,添加需要使用的Lib文件(通常情况下,使用前三个):opencv_core opencv_imgproc opencv_highgui opencv_ml opencv_video opencv_features2d opencv_calib3d opencv_objdetect opencv_contrib opencv_legacy opencv_flann6. 重新build all工程,大功告成~~*^_^*本文将会在以后继续添加&opencv for IOS 的使用介绍,敬请期待...
转载本文请联系原作者获取授权,同时请注明本文来自冯博远科学网博客。链接地址:
上一篇:下一篇:
当前推荐数:3
推荐到博客首页
评论 ( 个评论)
扫一扫,分享此博文
作者的其他最新博文
热门博文导读
Powered by
Copyright &>> 内侧轴和边界提取的二进制图像使用 Visual C 和 OpenCV
内侧轴和边界提取的二进制图像使用 Visual C 和 OpenCV
所属分类:
下载地址:
MC++_openCV_Medial axis bounda文件大小:1.28 MB
分享有礼! 》
请点击右侧的分享按钮,把本代码分享到各社交媒体。
通过您的分享链接访问Codeforge,每来2个新的IP,您将获得0.1 积分的奖励。
通过您的分享链接,每成功注册一个用户,该用户在Codeforge上所获得的每1个积分,您都将获得0.2 积分的分成奖励。
此代码使用 visual c + + 中与 opencv 提取的中轴线和边界的二进制图像。若要查看结果,请首先选择您的文件从文件菜单,然后按提取边界或内侧轴按钮以执行所需的任务。
Sponsored links
源码文件列表
温馨提示: 点击源码文件名可预览文件内容哦 ^_^
f.bmp183.65 kB03-03-12 08:50
f1.bmp74.70 kB22-02-12 23:56
f2.bmp67.80 kB23-02-12 00:00
f3.bmp27.37 kB22-02-12 23:36
f4.bmp2.26 kB22-02-12 23:35
f5.bmp18.65 kB23-02-12 00:04
f6.bmp12.36 kB23-02-12 00:12
app.ico1.05 kB23-07-03 17:52
1.27 kB29-02-12 04:33
1.30 kB29-02-12 04:33
10.47 kB03-03-12 12:38
Form1.resx5.88 kB03-03-12 11:33
466.00 B01-03-12 06:47
MC++_openCV.vcproj5.85 kB01-03-12 08:37
MC++_openCV.vcproj.NAVID-LAPTOP.navid.user1.39 kB05-03-12 12:05
MC++_openCV.vcproj.navid-PC.navid.user1.39 kB11-02-14 14:10
MC++_openCV.vcproj.OMID-PC.omid.user1.38 kB03-03-12 23:44
1.32 kB29-02-12 04:33
app.res1.18 kB05-02-13 09:13
AssemblyInfo.obj6.60 kB05-02-13 09:13
14.80 kB05-02-13 09:13
MC++_openCV.exe.intermediate.manifest616.00 B05-02-13 09:13
MC++_openCV.obj1.73 MB05-02-13 09:13
MC++_openCV.pch3.13 MB05-02-13 09:13
MC_openCV.Form1.resources180.00 B05-02-13 09:13
mt.dep66.00 B05-02-13 09:13
stdafx.obj16.60 kB05-02-13 09:13
vc90.idb251.00 kB05-02-13 09:13
vc90.pdb556.00 kB05-02-13 09:13
91.00 B29-02-12 04:33
211.00 B29-02-12 04:33
242.00 B29-02-12 04:33
MC++_openCV.sln899.00 B29-02-12 04:33
MC++_openCV.suo29.00 kB11-02-14 14:10
MC++_openCV.exe97.50 kB05-02-13 09:13
MC++_openCV.pdb899.00 kB05-02-13 09:13
&Debug&0.00 B03-03-12 23:49
&Release&0.00 B09-02-15 16:36
&Debug&0.00 B03-03-12 23:49
&MC++_openCV&0.00 B09-02-15 16:36
&Release&0.00 B09-02-15 16:36
&MC++_openCV_test&0.00 B09-02-15 16:36
(提交有效评论获得积分)
评论内容不能少于15个字,不要超出160个字。
评价成功,多谢!
下载MC++_openCV_Medial axis bounda
CodeForge积分(原CF币)全新升级,功能更强大,使用更便捷,不仅可以用来下载海量源代码马上还可兑换精美小礼品了
您的积分不足,优惠套餐快速获取 30 积分
10积分 / ¥100
30积分 / ¥200原价 ¥300 元
100积分 / ¥500原价 ¥1000 元
订单支付完成后,积分将自动加入到您的账号。以下是优惠期的人民币价格,优惠期过后将恢复美元价格。
支付宝支付宝付款
微信钱包微信付款
更多付款方式:、
您本次下载所消耗的积分将转交上传作者。
同一源码,30天内重复下载,只扣除一次积分。
鲁ICP备号-3 runtime:Elapsed:208.685ms - init:0.1;find:1.0;t:0.6;tags:0.2;related:111.7;comment:0.3; 27.69
登录 CodeForge
还没有CodeForge账号?
Switch to the English version?
^_^"呃 ...
Sorry!这位大神很神秘,未开通博客呢,请浏览一下其他的吧OPENCV用c++的方式加载图片_百度文库
两大类热门资源免费畅读
续费一年阅读会员,立省24元!
OPENCV用c++的方式加载图片
阅读已结束,下载本文需要
想免费下载更多文档?
定制HR最喜欢的简历
下载文档到电脑,同时保存到云知识,更方便管理
还剩2页未读,继续阅读
定制HR最喜欢的简历
你可能喜欢Uninitialized string offset: 2 in /home/wwwroot/www.bolearn.com/read.php on line 81
学习OpenCV_图文
学习 OpenCV.doc
出版前言........................... VI 译者序.. ............................ XI 写在前面的话.................. XIII 前言...... .......................... XV 第 1 章 概述...................... 1 什么是 OpenCV.................. 1 OpenC V 的应用领域.......... 1 什么是计算机视觉.............. 2 OpenC V 的起源.................. 6 下载和安装 OpenC V.......... 8 通过 SVN 获取最新的 OpenCV 代码........... 11 更多 OpenC V 文档........... 12 OpenC V 的结构和内容.... 14 移植性.. 16 练习. .... 16 第 2 章 OpenCV 入门..... 18 开始准备............................ 18 初试牛刀―― 显示图像... 19 第二个程序―― 播放 AVI 视频.................... 21 视频播放控制.................... 23 一个简单的变换................ 26 一个复杂一点的变换........ 28 从摄像机读入数据............ 30 写入 AVI 视频文件.......... 31 小结. .... 33 练习. .... 34 第 3 章 初探 OpenCV..... 35 OpenC V 的基本数据类型 35
第 1 页 共 110 页
学习 OpenCV.doc
CvMat 矩阵结构................ 38 IplImage 数据结构............. 48 矩阵和图像操作................ 54 绘图. .... 91 数据存储............................ 98 集成性能基元.................. 102 小结. .. 103 练习. .. 103 第 4 章 细说 HighGUI...... 106 一个可移植的图形工具包............................ 106 创建窗口.......................... 107 载入图像.......................... 108 显示图像.......................... 110 视频的处理...................... 120 ConvertImage 函数.......... 125 练习. .. 126 第 5 章 图像处理.......... 128 综述. .. 128 平滑处理.......................... 128 图像形态学...................... 134 漫水填充算法.................. 146 尺寸调整.......................... 149 图像金字塔...................... 150 阈值化 155 练习. .. 162 第 6 章 图像变换.......... 165 概述. .. 165 卷积. .. 165
第 2 页 共 110 页
学习 OpenCV.doc
梯度和 Sobel 导数........... 169 拉普拉斯变换.................. 172 Canny 算子...................... 173 霍夫变换.......................... 175 重映射 183 拉伸、收缩、扭曲和旋转............................ 185 CartToPolar 与 PolarToCart........................... 196 LogPolar........................... 197 离散傅里叶变换(DFT)... 200 离散余弦变换(DCT)....... 205 积分图像.......................... 206 距离变换.......................... 208 直方图均衡化.................. 211 练习. .. 213 第 7 章 直方图与匹配... 216 直方图的基本数据结构.. 219 访问直方图...................... 221 直方图的基本操作.......... 223 一些更复杂的策略.......... 231 练习. .. 244 第 8 章 轮廓.................. 246 内存. .. 246 序列. .. 248 查找轮廓.......................... 259 Freeman 链码................... 266 轮廓例子.......................... 268 另一个轮廓例子.............. 270 深入分析轮廓.................. 271
第 3 页 共 110 页
学习 OpenCV.doc
轮廓的匹配...................... 279 练习. .. 290 第 9 章 图像局部与分割................. 293 局部与分割...................... 293 背景减除.......................... 294 分水岭算法...................... 328 用 Inpainting 修补图像.... 329 均值漂移分割.................. 331 Delaunay 三角剖分和 Voronoi 划分........... 333 练习. .. 347 第 10 章 跟踪与运动..... 350 跟踪基础.......................... 350 寻找角点.......................... 351 亚像素级角点.................. 353 不变特征.......................... 355 光流. .. 356 mean-shift 和 camshift 跟踪........................... 371 运动模板.......................... 376 预估器 383 condensation 算法............ 399 练习. .. 403 第 11 章 摄像机模型与标定........... 406 摄像机模型...................... 407 标定. .. 414 矫正. .. 430 一次完成标定.................. 432 罗德里格斯变换.............. 437 练习. .. 438
第 4 页 共 110 页
学习 OpenCV.doc
第 12 章 投影与三维视觉............... 441 投影. .. 441 仿射变换和透视变换...... 443 POSIT:3D 姿态估计.... 449 立体成像.......................... 452 来自运动的结构.............. 493 二维和三维下的直线拟合............................ 494 练习. .. 498 第 13 章 机器学习........ 499 什么是机器学习.............. 499 OpenC V 机器学习算法.. 502 Mahalanobis 距离............ 516 K 均值 519 朴素贝叶斯分类.............. 524 二叉决策树...................... 527 boosting............................ 537 随机森林.......................... 543 人脸识别和 Haar 分类器 549 其他机器学习算法.......... 559 练习. .. 560 第 14 章 OpenCV 的未来............... 564 过去与未来...................... 564 发展方向.......................... 565 OpenC V 与艺术家.......... 568 后记. .. 570 参考文献......................... 571 索引...... ......................... 586 关于作者和译者.............. 599
第 5 页 共 110 页
学习 OpenCV.doc
封面图片......................... 601
第 1 章 概述
什么是 OpenCV
OpenCV 是一个开源(参见 http://opensource.org)的计算机视觉库,项目主页为 http://SourceForge.net/projects/opencvlibrary。OpenCV 采用 C/C++语言编 写,可以运行在 Linux/Windows/Mac 等操作系统上。OpenCV 还提供了 Python、R uby、MATLAB 以及其他语言的接口。 OpenCV 的设计目标是执行速度尽量快,主要关注实时应用。它采用优化的 C 代 码编写,能够充分 利用多核处理器的优势。如果是希 望在 Intel 平台上得到更快 的处理速度,可以购买 Intel 的高性能多媒体 函数库 IPP(Integrated Performa nce Primitives)。IPP 库包含许多从底层优化的函数,这些函数涵盖多个应用 领域。如果系统已经安装了 IPP 库,OpenCV 会在运行时 自动使用相应的 IPP 库。 OpenCV 的一个目标是构建一个简单易用的计算机视觉框架,以帮助开发人员更 便捷地设计更复杂的计算机视觉相关应用程序。OpenCV 包含的函数有 500 多个, 覆盖了计算机视觉的许多应用领域,如工厂产品检测、医学成像 、信息安全、用 户界面、摄像机标定、立体视觉和机器人等。因为计算机视觉和机器学习密切相 关,所以 OpenCV 还提供了 MLL(Machine Learning Library)机器学习库。该机 器学习库侧重于统计方面的模式识别和聚类(clustering)。MLL 除了用在视觉相 关的任务中,还可以方便地应用于其他的机器学习场合。
OpenCV 的应用领域
大多数计算机科学家和程序员已经意识到计算机视觉的重要作用。但是很少有人 知道计算机视觉的所有应用。例如,大多 数人或多或少地知道计算机视觉可用在 监控方面,也知道视 觉被越来越多地用在网络图像和视频方面。少数人也了解计 算机视觉在游戏界面方面的应用。但 是很少有人了解大多数航空和街道地图图像 (如 Google 的 Street View)也大量使用计算机定标和图像拼接技术。一些人知 道安全监控、无人 飞行器或生物医学分析等方面的应用,但是很少人知道机器视 觉是多么广泛地被用在工厂中:差不 多所有的大规模制造的产品都在流水线上的 某个环节上自动使用视觉检 测。 【1~2】 OpenCV 所有的开放源代码协议允许你使用 OpenCV 的全部代码或者 OpenCV 的部 分代码生成商业产品。使用了 OpenCV 后,你不必对公众开放自己的源代码或改 善后的算法,虽然我们非常希望你能够开放源代码。许多公司(IBM,Microsoft, Intel,SONY,Siemens 和 Google 等其他公司)和研究单 位(例如斯坦福大学、MI T、CMU、剑桥大学和 INRIA)中的人都广泛使用 OpenCV,其部分原因是 OpenCV 采用了这个宽松的协议。Yahoo groups 里有一个 OpenCV 论坛(http://groups.y ahoo.com/group/OpenCV),用户可以在此发帖提问和讨论;该论坛大约有 20 00 0 个会员。OpenCV 在全世界广受欢迎,在中国、日本、俄罗斯、欧洲和以色列都 有庞大的用户群。
第 6 页 共 110 页
学习 OpenCV.doc
自从 OpenCV 在 1999 年 1 月发 布 alpha 版本开始,它就被广泛用在许多应用领域、 产品和研究成果中。相关应用包括卫星地图和电子地图的拼接,扫描图像的对齐, 医学图像去噪(消噪或滤波),图像中的物体分析,安全和入侵检测系统,自动监 视和安全系统,制造业中的产品质量检测系统,摄像机标定 ,军事应用,无人飞 行器,无人汽车和无人水下机器人。将视觉识别技术用在声谱图上,OpenCV 可 以进行声音和音乐识别。在斯坦福大学的 Stanley 机器人项目中,OpenCV 是其 视觉系统的关键部分。Stanley 在 DARPA 机器人沙漠挑战赛中,赢得了二百万美 元奖金[Thrun06]。
什么是计算机视觉
计算机视觉[①]是将来自静止图像或视频的数据转换成一个决策或者一种新的 表达方式的过程,所有的这些转换都是为了达到某个目标。输入数据可以包含一 些辅助信息,如“摄像机架在汽车上”或“激光扫描仪在 1 米处发现一个物体” 。 最终的决策可能是“场景中有一个人”或“在这个切片中有 14 个肿瘤细胞” 。 一种新的表达方式可以是将一张彩色照片转为灰度照片,或者从图像序列中去除 摄像机晃动影响。 因为人类是视觉动物,所以会误以为可以很容易地实现计算机视觉。当你凝视图 像时,从中找到一辆 汽车会很困难么?你凭直觉会觉得很容易。人脑将视觉信号 划分入很多个通道,将各种不同的信息输入你的大脑。你的大脑有一个关注系统, 会根据任务识别出图像的重要部分,并做重点分析,而其他部分则分析得较少 。 在人类视觉流中存在大量的反馈,但是目前我们对之了解甚少。肌肉控制的传感 器以及其他所有传感器的输入信息之间存在广泛的关联,这使得大脑可以依赖从 出生以来所学到的信息。大脑中的反馈在信息处理的各个阶段都存在,在传感器 硬件(眼睛)中也存在。在眼睛中通过反馈来调节通过瞳孔的进光量,以及调节视 网膜表面上的接收单 元。 【2~3 】 在计算机视觉系统中,计算机接接收 到的是来自摄像机或者磁盘文件的一个数值 矩阵。一般来说,没有内置的模式识别系统,没有自动控制的对焦和光圈,没有 多年来经验的积累。视觉系统通常 很低级。图 1-1 显示了一辆汽车的图像。在此 图中,我们可以看到车的一侧有一个反光镜,而计算机“ 看”到的只是一个数值 的矩阵。矩阵中的每个数值都有很大的噪声成分,所以它仅仅给出很少的信息 ,
第 7 页 共 110 页
学习 OpenCV.doc
这个数值矩阵就是计算机“看”到的全部。我们的任务是 将这个具有噪声成分的 数值矩阵变成感知: “反光镜” 。图 1-2 形象地解释了为什么计算机视觉如此之 难。
图 1-1:对一个计算机来说,汽车的反光镜只是一个数值矩阵
图 1-2:视觉问题的病态本质:随着视点的变化,物体的二维外观会变化很大
实际上,计算机视觉问题比前面我们提到的更糟糕 ,它是不可解的。给出三维世 界的二维视图,是没有固定方法来重建出三维信 息的。在理论上,此类病态问题
第 8 页 共 110 页
学习 OpenCV.doc
没有惟一和确定的解。即使数据很完美,同一张二维图像也可以表示多种三维场 景。然而,如前面提到的,数据会被噪声和形 变影响。这些影响来自真实世界的 变化(天气、光线、反射、运动),镜头和机械结构的不完美,传感器上的长时间 感应(运动模糊),传感 器上和其他电子器件上的电子噪声,以及图像采集后的图 像压缩引入的变化。有如此多令人头疼的问题,我们如何取得进 展? 【3~4】
设计实际系统时,为了克服视觉传感器的限制,通常会使用一些其他的上下文知 识。考虑这样一个例子,移动机器人在室内寻找并捡起订书机。机器人可以利用 这个先验知识:可在办公室内发现桌子,订书机最可能在桌子上被找到。这给出 了一个隐含的尺寸参考或参照,也就是订书机能够放在桌子上。这也可以用于消 除在不可能的地方(例如在天花板或者窗户上)错误识别出订书机的可能性。机器 人也完全可以忽略一个 200 英尺大小的跟订书机形状类似的广告飞艇,因为飞艇 周围没有桌子的木纹背景。与之相 反,在图像检索中,数据库中的所有订书机图 像都是对真正的订书机拍摄的,而且 尺寸很大和形状不规划的订书机图像一般不 可能被拍到。也就是拍摄者一般只拍摄真正的、普通大小的订书机图像。而且人 们拍照时一般会将被拍物体置于中心,且将物体放在最能表现其特征的方向上 。 因此在由人拍摄的图像中,具有相当多的隐含信息。
【4~5】 我们也可以使用机器学习技术对上下文信息进行显式建模。隐含的变量(例如物 体大小、重力方向及其他变量)都可以通过标记好的训练数据里的数值来校正。 或者,也可以通过其 他的传感器来测量隐含的变量。使用激光扫描仪可以精确测 量出一个物体的大小。计算机视觉面临的另一个难题是噪声问题。我们一般使用 统计的方法来克服噪声。例如,一般来说 不可能通过比较一个点和它紧密相邻的 点来检测图像里的边缘。但是如果观察一个局部区域的统计特征,边缘检测会变 得容易些。由局部区域卷积的响应连成的点串,构成边缘。另外可以通过时间维 度上的统计来抑制噪声。还有一些 其他的技术,可以从数据中学习显式模型,来
第 9 页 共 110 页
学习 OpenCV.doc
解决噪声和畸变问题。例如镜头畸变,可 以通过学习一个 简单多项式模型的参数 来描述这种畸变,然后可以几乎完全校正这种畸变。 计算机视觉拟根据摄像机数据来采取行动或者做出决策,这样的行动或决策是在 一个指特定目的或任务的环境中来解决。我们从图像去除噪声和损坏区域,可以 让监控系统在有人爬过栅栏时给出报警,或者在一个游乐园里监控系统能够数出 总共有多少人通过了某个区域。在办 公室巡游的机器人的视觉软件所采用的方法 与固定摄像机的不同,因为这两个系统有不同的应用环境和目标。通用的规律是: 对计算机视觉应用环境的约束越多,则越能够使用这些约束来简化问题,从而使 最终的解决方案越可靠。 OpenCV 的目标是为解决计算机视觉问题提供基本工具。在有些情况下,它提供 的高层函数可以高效地解决计算机视觉中的一些很复杂的问题。当没有高层函数 时,它提供的基本函数足够为大多数计算机视觉问题创建一个完整的解决方案 。 对于后者,有几个经过检验且可靠的使用 OpenCV 的方法;所有这些方法都是首 先大量使用 OpenCV 函数来解决问题。一旦设计出解决方案的第一个版本,便会 了解它的不足,然后可以使用自己的代码和知识来解决(更为广知的一点是“解 决实际遇到的问题,而不是你想像出来的问题”)。你可以使用第一个版本的解 决方案作为一个基准,用之评价解决方案的改进程度。解决 方案所存在的不足可 以通过系统所用的环境限制来解 决。 【5~6】
OpenCV 的起源
OpenCV 诞生于 Intel 研究中心,其目的是为了促进 CPU 密集型应用。为了达到 这一目的,Intel 启动了多个项目,包括实时光线追踪和三维显示墙。一个在 I ntel 工作的 OpenCV 作者在访问一些大学时,注意到许多顶尖大学中的研究组(如 MIT 媒体实验室)拥有很好的内部使用的开放计算机视觉库―― (在学生们之间 互相传播的代码),这会帮助一个新生从高的起点开始他/她的计算机视觉研究 。 这样一个新生可以在以前的基础上继续开始研究,而不用从底层写基本函数。
第 10 页 共 110 页
学习 OpenCV.doc
因此,OpenCV 的目的是开发一个普遍可用的计算机视觉库。在 Intel 的性能库 团队的帮助下[②],OpenCV 实现了一些核心代码以及算法,并发给 Intel 俄罗 斯的库团队。这就是 OpenCV 的诞生之地:在与软件性能库团队的合作下,它开 始于 Intel 的研究中心,并在俄罗斯得到实现和优化。 俄罗斯团队的主要负责人是 Vadim Pisarevsky,他负责管理项目、写代码并优 化 OpenCV 的大部分代码,在 OpenCV 中很大一部分功劳都属于他。跟他一起,V ictor Eruhimov 帮助开发了早期的架构,Valery Kuriakin 管理俄罗斯实验室并 提供了很大的支持。在开始时,OpenCV 有以下三大目标。
l 为基本的视觉应用提供开放且优化的源代码,以促进视觉研究的发展。能有效地避免“闭门造车”。 l 通过提供一个通用的架构来传播视觉知识,开发者可以在这个架构上继续开展工作,所以代码应该是非常易读的且可 改写。 l 本库采用的协议不要求商业产品继续开放代码,这使得可移植的、性能被优化的代码可以自由获取,可以促进基于视 觉的商业应用的发展。
这些目标说明了 OpenCV 的缘起。计算机视觉应用的发展会增加对快速处理器的 需求。与单独销售软件相比,促进处理器的升级会为 Intel 带来更多收入。这也 许是为什么这个开放且免费的库出现在一家硬件生产企业中,而不是在一家软件 公司中。从某种程度上说,在一家硬件公司里,在软件方面会有更多创新的空 间。
【6】 任何开放源代码的努力方面,达到一 定的规模使项目自己能够发展是非常重要的。 目前 OpenCV 已经有大约二百万的下载量,这个数字仍然在以平均每个月 26 000 的下载量递增。Open CV 用户组大约有 20 000 个会员。OpenCV 吸纳了许多用户的 贡献,核心开发工作已经从 Intel 转移到别处[③]。OpenCV 过去的开发历程如 图 1-3 所示。在发展中,OpenCV 受到网络经济泡沫破裂的影响,也受到无数次 管理和发展方向变化的影响。在这些变故中,OpenCV 曾经有多次缺乏 Intel 公 司人员的支持。然而 ,随着多核时代的到来,以及计算机视觉的更多应用的出现, OpenCV 的价值开始提升。现在 OpenCV 在几个研究所中的开发都很活跃,所以不
第 11 页 共 110 页
学习 OpenCV.doc
久应该会看到更多的功能出现,如 多摄像机标定、深度信息感知、视觉与激光扫 描的融合、更好的模式识别算法,同时还会支持 机器人视觉的需求。关于 OpenC V 未来的发展,请参考第 14 章。
图 1-3:OpenC V 发展路线图
用 IPP 给 OpenCV 加速
因为 OpenCV 曾由 Intel 性能基元(IPP)团队主持,而且 几个主要开发者都与 IPP 团队保持着良好的关系,所以 OpenCV 利用了 IPP 高度手工优化的代码来实现加 速。使用 IPP 获得的提速是非常显著的。图 1-4 比较了另 外两个视觉库 LTI[LTI] 和 VXL[VXL]与 OpenCV 以及 IPP 优化的 OpenCV 的性能。请注意,性能是 OpenCV 追求的一个关键目标;它需要实时运行代码的能力。 OpenCV 使用优化了的 C 和 C++代码实现。它对 IPP 不存在任何依赖。但如果安装 了 IPP,那么 OpenCV 将会通过自动载入 IPP 动态链接库来获取 IPP 的优势,来 提升速 度。 【6~7】
第 12 页 共 110 页
学习 OpenCV.doc
图 1-4:另外两个视觉库(LTI 和 VXL)与 OpenCV(不使用和使用 IPP)的四个不同性能指标的比较:每个指标的四个柱图分 别表示四个库的得分,得分与运行时间成正比;在所有指标中,OpenCV 均优于其他的两个库,且用 IPP 优化的 OpenC V 优于没有使用 IPP 优化的 OpenCV
OpenCV 属于谁
虽然 OpenCV 项目是 Intel 发起的,但这个库一直致力于促进商业和研究使用。 它是开放源代码且免费的,无论是商业使用还是科研使用,OpenCV 的代码可用 于或者嵌入(整体或部分)其他的应用程序中。它不 强迫你开放或者免费发放你的 源代码。它也不要求你将改进的部分提交到 OpenCV 库中――但我们希望你能够 提交。
下载和安装 OpenCV
OpenCV 项目主页在 SourceForge 网站 http://SourceForge.net/projects/ ope ncvlibrary,对应的 Wiki 在 http://opencv.willowgarage.com。对于 Linux 系 统,源代码发布文件为 opencv-1.0.0.tar.gz;对于 Windows 系统,则为 OpenC V_1.0.exe 安装程序。然而,最新的版本始终都在 SourceForge 的 SV N 仓库 中 。
下载 OpenCV 库之后,就可以安装了。Linux 和 Mac OS 系统的安装细节可以查看... /opencv/目录下的 INSTALL 文本文件中的说明。INSTALL 文件中还描述了如何编 译 OpenCV 和运行测试程序。对于 OpenCV 开发人员,INSTALL 还列出了所需的 a utoconf、automake、libtool 和 swig 其他开发工 具。 Windows 从 SourceForge 网站下载 OpenCV 安装程序,然后运行安装程序。安装程序将安 装 OpenCV,注册 DirectShow filter,然后进行一些安装后的处理。现在你就可 以使用 OpenCV 了。你还可以进入目录.../opencv/_make,使用 MSVC++或者 MSV C.NET 2005 打开 opencv.sln,或者使用低版本的 MSVC++打开 opencv.dsw,然后 生成 Debug 版的库,也可以重新生成 Release 版的库[④]。 【8~9】
第 13 页 共 110 页
学习 OpenCV.doc
如果需要使用 IPP 的优化功能,首先需要从 Intel 网站(http://www.intel.com /soft ware/products/ipp/index.htm)获得 IPP 并安装;请使用 5.1 或更新的版 本。请确认二进制文件路径(例如 c:/program files/intel/ipp/5.1/ia32/bin ) 被添加到系统环境变量 PATH 中。现在 OpenCV 就能够自动探测到 IPP,并在运行 时装载 IPP 了(详细信息请参考第 3 章)。 Linux 因为在 Linux 系统的各个发行版(SuSE,Debian,Ubuntu 等)的 GCC 和 GLIBC 版 本并不一样,OpenCV 的 Linux 版本并不包含可直接使用的二进制库。如果发行 版没有提供 OpenCV,则需要从源代码重新编译 OpenCV,具体的细节请参考.../ opencv/INSTALL 文件。 如果要编译库和演示程序,需要版本为 2.x 或更高版本的 GTK+及其头文件。除 此之外还要需要具有开发文件的 pkgconfig, libpng, zlib, libjpeg, libtif f 和 libjasper。同时 还要安装版本为 2.3、2.4 或 2.5 的 Python 及其头文件(开 发包)。同时还需要依赖 ffmpeg 0.4.9-pre1 或更高的 版本中 libavcodec 和 lib av*系列的库,ffmpeg 的最新版可以用以下命令获取:svn checkout svn://svn. mplayerhq.hu/ffmpeg/trunk ffmpeg。 从 http://ffmpeg.mplayerhq.hu/download.html 下载 ffmpeg 库[⑤],ffmpeg 库的授权协议为 GNU 宽通用公共许可证(LGPL)。非 GPL 软件(如 OpenCV)使用 ff mpeg 库,需要生成和调用共享的 ffmpeg 库:
$& ./configure --enable-shared $& make $& sudo make install
编译完成后会生成以下系列库文件:/usr/local/lib/libavcodec.so.*,/usr/ local/lib/libavformat.so.*,/usr/local/lib/libavutil.so.*,及其对应的 头文件/usr/local/include/libav *。 【9】 下载了 OpenCV 后就可以编译 OpenCV 了[⑥]:
第 14 页 共 110 页
学习 OpenCV.doc
$& ./configure $& make $& sudo make install $& sudo ldconfig
安装完成后,OpenCV 会被默认安装在以下目录:/usr/local/lib/ 和 /usr/loc al/include/opencv/。因此,用户需要将/usr/local/lib/添加到/etc/ld.so.c onf 文件(之后需要执行 ldconfig 命令),或者将该路径添加到 LD_LIBRARY_PAT H 环境变量中。 同样在 Linux 平台也可以用 IPP 给 OpenCV 加速,IPP 的安装细节在前面已经提 过。我们现在假设 IPP 安装在以下路径:/opt/intel/ipp/5.1/ia32/。修改初始 化配置文件,添加&your install_path&/bin/和&your install_path&/bin/lin ux32 到 LD_LIBRARY_PATH 环境变量(可以直接编辑.bashrc 配置文件):
LD_LIBRARY_PATH=/opt/intel/ipp/5.1/ia32/bin:/opt/intel/ipp/5.1/ia32/bin/linux32:$LD_LIBRA RY_PATH export LD_LIBRARY_PATH
另一个方法是将&your install_path&/bin 和&your install_path&/bin/linux 32 添加到/etc/ld.so.conf 文件,每个文件占一行,完成 后在 root 权限下(也可 以使用 sudo 命令)执行 ldconfig 命令。 现在 OpenCV 就可以找到并能使用 IPP 的共享库了,具体的细节请参考/opencv/ INSTALL。 Mac OS X 当写此书的时候,所有的功能都可以在 Mac OS X 下使用,但是仍然有一些限制 (如 AVI 文件的写操作);文件.../opencv/INSTALL 中详细描述了这些限制。 在 Mac OS X 下的编译需求和编译步骤跟 Linux 下类似,但是有如下不同。
l 默认情况下是使用 Carbon 而不是 GTK+。 l 默认情况下是使用 QuickTime 而不是 ffmpeg。 l p kg-config 是非必需的(它只在脚本 samplels/c/build_all.sh 中用到) l 默认情况下不支持 RPM 和 ldconfig。使用命令 configure+make+sudo make install 来编译和安装 OpenCV;如果不 是使用./configure --prefix=/usr 命令来配置的话,需要更新 DYLD_LIBRARY_PATH 变量。
第 15 页 共 110 页
学习 OpenCV.doc
如果要使用全部功能,需要使用 darwinports 来安装 libpng、libtiff、libjpe g 和 libjasper,然后使它们能够被脚本./configure 检 测到(详细帮助请运行./ configure --help)。对于 大多数信息,可以参考 OpenCV Wiki (网址为 http:// opencv.willowgarage.com/)和 Mac 相关的页面(网址为 http://opencv.willowg arage. com/Mac_OS_X_OpenCV_Port)。
通过 SVN 获取最新的 OpenCV 代码
OpenCV 是一个相对活跃的开发项目,如果提交了 bug 的详细描述以及出错的代 码,该 bug 会被很快修复。然而,OpenCV 一般一年才会发布一个或两个官方版 本。如果用 OpenCV 开发比较重要的应用,你可能想获得修复了最新 bug 的最新 OpenCV 代码。如果要获取 OpenCV 的最新代码,需要 通过 SourceForge 网站上的 OpenCV 库的 SVN(Subversion)获 得。 0~11】 这里并不是一个 SVN 的完整教程。如果你参与过其他的开源项目,也许很熟悉 S VN。如果不了解 SVN,可以参考 Ben Collins-Sussman 等人所著的 Version Con trol with Subversion(O’Reilly 出版)。SVN 的命令行 客户端一般被打包在 Li nux、OS X 和大部分类 UNIX 系统中。对于 Windows 系统的用户,可以选择 Tort oiseSVN(http://tortoisesvn.tigris.org/)客户端,很多命令被集成到 Window s 资源管理器的右键菜单中,使用很方便。 对于 Windows 用户,可使用 TortoiseSVN 检出最新源代码,检出地址为 https:/ /opencvlibrary.svn.sourceforge.net/svnroot/opencvlibrary/trunk。 对于 Linux 用户,可以使用如下命令检出最新源代码:
svn co https://opencvlibrary.svn.sourceforge.net/svnroot/opencvlibrary/trunk 更多 OpenCV 文档
OpenCV 的主要文档是随源代码一起发布的 HTML 帮助文件。除此之外,网上的参 考文档还有 OpenCV Wiki 网站和以前的 HTML 帮助。
HTML 帮助文档
第 16 页 共 110 页
学习 OpenCV.doc
安装 OpenCV 后,在.../opencv/docs 子目录中有相应的 HTML 格式的 帮 助文 件 , 打开 index.htm 文件,其中包含以下链接。 CXCORE 包含数据结构、矩阵运算、数据变换、对象持久(object p ersistence)、内存管 理、错误处理、动态装载、绘图、文本和基本的数学功能等。 CV 包含图像处理、图像结构分析、运动描述和跟踪、模式识别和摄像机标定。 Machine Learning (ML) 包含许多聚类、分类和数据分析函数。 HighGUI 包含图形用户界面和图像/视频的读/写。 CVCAM 摄像机接口,在 OpenCV 1.0 以后的版本中被移除。 Haartraining 如何训练 boosted 级联物体分类器。文档在文件 .../opencv/apps/HaarTraini ng/ doc/haartraining.htm 中。 目录.../opencv/docs 中还有一个 IPLMAN.pdf 文件,它是 OpenCV 的 早 期文 档 。 这个文档已经过时,阅读时一定要注意。但是这个文档中详细描述了一些算法以 及某些算法中应该使用何种类型的图像。当然,本书是详细描述这些图像和算法 的最佳参考资料。
Wiki 帮助文档
OpenCV 的文档 Wiki 所包含的内容比 OpenCV 安装文件自带的 HTML 帮助更新,并 包含自带文档没有的一些内容。Wiki 网址为 http://opencv.willowgarage.com , 它包含以下内容:
l 用 Eclipse 集成开发环境编译 OpenCV 的帮助 l 使用 OpenCV 进行人脸识别 l 视频监控 l 使用向导
第 17 页 共 110 页
学习 OpenCV.doc
l 摄像机支持 l 中文和韩文网站链接
另外一个 Wiki 地址为 http://opencv.willowgarage.com/wiki/CvAux,是下一 节“OpenCV 架构 和内容”提到的辅助函数的惟一 文档。CvAux 模块包含以下信息:
l 双目匹配 l 多摄像机情况下的视点渐变 l 立体视觉中的三维跟踪 l 用于物体识别的 PCA 方法 l 嵌入隐马尔可夫模型(HMM)
OpenCV 中文的 Wiki 地址为 http://www.opencv.org.cn/。
刚才提到的帮助文档并没有解释下面的问题: l 哪些类型(浮点、整数、单字节;1-3 通道)的图像适用于某个函数? l 哪些函数可以以 in place 模式(输入和输出使用同一个图像结构)调用? l 一些复杂函数的调用细节(如 contours)? l 目录…/opencv/samples/c/中各个例子的运行细节? l 为什么要这样使用函数,而不仅仅是如何使用? l 怎么样设置某些函数的参数?
本书的目的是解答上述问题。
OpenCV 的结构和内容
OpenCV 主体分为五个模块,其中四个模块如图 1-5 所示。OpenCV 的 CV 模块包含 基本的图像处理函数和高级的计算机视觉算法。ML 是机器学习库,包含一些基 于统计的分类和聚类工具。HighGUI 包含图像和视频输入/输出的函数。CXCore 包含 OpenCV 的一些基本数据结构和相关函数。
第 18 页 共 110 页
学习 OpenCV.doc
图 1-5:OpenC V 的基本结构
图 1-5 中并没有包含 CvAux 模块,该模块中一般存放一些 即将被淘汰的算法和函 数(如基于嵌入式隐马尔可夫模型的人脸识别算法),同时还有 一些新出现的实验 性的算法和函数(如背景和前景的分割)。CvAux 在 Wiki 中并没有很完整的文档, 而在.../opencv/docs 子目录下的 CvAux 文档也不是很完整。CvAux 包含以下一 些 内容。
l 特征物体,它是一个模式识别领域里用于降低计算量的方法,本质上,依然是模板匹配。 l 一维和二维隐马尔可夫模型(HMM),它是一个基于统计的识别方法,用动态规划来求解。 l 嵌入式 HMM(一个父 HMM 的观测量本身也符合 HMM) l 通过立体视觉来实现的动作识别 l Delaunay 三角划分、序列等方法的扩展 l 立体视觉 l 基于轮廓线的形状匹配 l 纹理描述 l 眼睛和嘴跟踪 l 3D 跟踪 l 寻找场景中的物体的骨架(中心线) l 通过两个不同视角的图像合成中间图像 l 前景/背景分割 l 视频监控(请参考 Wiki 的 FAQ 获得更多资料)
第 19 页 共 110 页
学习 OpenCV.doc
l 摄像机标定的 C++类(C 函数和引擎已经在 CV 模块中)
未来一些特性可能被合并到 CV 模块,还有一些可能永远留在 CvAux 中。 【13~14】
OpenCV 被设计为可移植的库。它的代码可以用 Borland C++, MS VC++, Intel 等编译器编译。为了使得跨平台更容易实现,C/C++代码必须按照通用的标准来 编写。图 1-6 显示了目前已知的可以运行 OpenCV 在各种系统平台。基于 32 位 I ntel 架构(IA32)的 Windows 系统支持最好,然后是 IA32 架构的 Linux 平台。对 于 Mac OS X 平台的支持,只有在 Apple 采用 Intel 处理器后才提上议程。(在 O S X 平台上的移植目前还不像 Windows 和 Linux 平台上一样成熟,但是已在快速 完善中。)成熟度次之的是在扩展内存上 64 位(EM64T)和 64 位 Intel 架构(IA64)。 最不成熟的是 Sun 的硬件和其他操作系统。
图 1-6:OpenC V 1.0 移植指南
如果某个 CPU 架构或操作系统没有出现在图 1-6 中,并不 意味着在那上面不能使 用 OpenCV。OpenCV 几乎可用于所有的商业系统,从 PowerPC Mac 到机器狗。Op enCV 同样可以很好的运行在 AMD 处理器上,IPP 也会采用 AMD 处理器里的多媒体 扩展技术(MMX 等)技术进行加 速。 14~ 15】
练习 1. 下载并安装最新的 OpenC V 版本,然后分别在 debug 和 release 模式下编译 OpenCV。
第 20 页 共 110 页
学习 OpenCV.doc
通过 SVN 下载 OpenCV 的最新代码,然后编译。 在三维信息转换为二维表示时,存在一些有歧义的描述,请描述至少三个歧义描述,并提供克服这些问题的方法。
第 2 章 OpenCV 入门
安装完 OpenCV 开发包后,我们的首要任务自然是立即 用它来做一些有趣的事情。 为此,还需要搭建编程环境。 在 Visual Studio 开发环境中,我们需要先创建一个项目(project),然后配置 好它的各项设置,以使 OpenCV 开发包中的 highgui.lib, cxcore.lib, ml.lib 和 cv.lib 库能被正确链接[⑦],并保证编译器的预处理器能搜索到“OpenCV ? /opencv/*/include”目录下的各个头文件。OpenCV 开发包中有许多&include& 目录,它们一般都位于“C:/program files/opencv/cv/include”[⑧], “? / opencv/cxcore/include”, “? /opencv/ml/include”和“? /opencv/otherl ibs/ highgui”路径下。完成这些工作后,便可 新建一个 C 程序文件并编写你的 第一个程序。
注意: 有一些重要的头文件可以使工作变得更轻松。在头文件“…/opencv/ cxcore/include/cxtypes.h”和“cxmisc.h” 中,包含许多有用的宏定义。使用这些宏,仅用一行程序便可完成结构体和数组的初始化、对链表进行排序 等工作。在编译时,有几个头文件非常重要,它们分别是:机器视觉中所要用到的“…/cv/include/cv.h”和“… /cxcore/include/cxcore.h”;I/O 操作中所要用到的“…/otherlibs/highgui/highgui.h”;机器学习中所要用到的 “…/ml/include/ml.h”。 初试牛刀―― 显示图像
OpenCV 开发包提供了读取各种类型的图像文件、视频内容以及摄像机输入的功 能。这些功能是 OpenCV 开发包中所包含的 HighGUI 工具集的一部分。我们将使 用其中的一些功能编写一段简单的程序,用以读取并在屏幕上显示一张图像。 如 例 2-1 所示。
例 2-1:用于从磁盘加载并在屏幕上显示一幅图像的简单 OpenCV 程序 #include &highgui.h&
第 21 页 共 110 页
学习 OpenCV.doc
int main( int argc, char** argv ) { IplImage* img = cvLoadImage( argv[1] ); cvNamedWindow( &Example1&, CV_WINDOW_AUTOSIZE ); cvShowImage( &Example1&, i mg ); cvWaitKey(0); cvReleaseImage( &img ); cvDestroyWindow( &Example1& ); }
当以上程序编译后,我们就可以在命令行模式下通过输入一个参数执行它。执行 时,该程序将向内存加载一幅图像,并将该图像显示于屏幕上,直至按下键盘的 任意一个键后它才关闭窗口并退出程序。下面我们将对以上代码做逐行分析,并 仔细讲解每个函数的用法以帮助读者理解这段程序。
IplImage* img = cvLoadImage( argv[1] );
该行程序的功能是将图像文件[⑨]加载至内存。cvLoadImage()函数是一个高层 调用接口,它通过文 件名确定被加载文件的格式;并且 该函数将自动分配图像数 据结构所需的内存。需要指出的是,cvLoadImage()函数可读取绝大多数格式类 型的图像文件,这些类型包括 BMP, DIB, JPEG, JPE, PNG, PBM, PGM, PPM, SR, RAS 和 TIFF。该函数执行完后将返回一个指针,此指针指向一块为描述该图像 文件的数据结构(IplImage)而分配的内存块。IplImage 结 构体将是我们在使用 O penCV 时会最常用到的数据结构。OpenCV 使用 IplImag e 结构体处理诸如单通道(s ingle-channel)、多通道(multichannel)、整型的(integer-valued)、浮点型的 (floating-point-valued)等所有类型的图像文件。
cvNamedWindow( &Example1&, C V_WINDOW_AUTOSIZE );
cvNamedWindow()函数也是一个高层调用接口,该函数由 HighGUI 库提供。cvNa medWindow()函数用于在屏幕上创建一个窗口,将被显示的图像包含于该窗口中。 函数的第一个参数指定了该窗口的窗口标题(本例中为&Example1&),如果要使用 HighGUI 库所提供的其他函数与该窗口进行交互时,我们将通过该参数值引用这 个窗口。
第 22 页 共 110 页
学习 OpenCV.doc
cvNamedWindow()函数的第二个参数定义了窗口的属性。该参数可被设置为 0(默 认值)或 CV_WINDOW_AUTOSIZE,设置为 0 时,窗口的大 小不会因图像的大小而改 变,图像只能在窗口中根据窗口的大小进行拉伸或缩放;而设置为 CV_WINDOW_A UTOSIZE 时,窗口则会根据图像的实际大小自动进行拉伸或缩放,以 容 纳 图像 。
cvShowImage( &Example1&, img );
只要有一个与某个图像文件相对应的 IplImage*类型的指针,我们就可以在一个 已创建好的窗口(使用 cvNamedWindow()函数创建)中使用 cvShowImage()函数显 示该图像。cvShowImage()函数通过设置其第一个参数确定在哪个已存在的窗口 中显示图像。cvShowImage()函数被调用时,该窗口将被重新绘制,并且图像也 会显示在窗口中。如果该窗口在创 建时被指定 CV_WINDOW_AUTOSIZE 标志作为 cv NamedWindow()函数的第二个参数,该窗口将根据图像的大小自动调整为与图像 一致。
cvWaitKey(0); 【17~18】
cvWaitKey()函数的功能是使程序暂停,等待用户触发一个按键操作。但如果将 该函数参数设为一个正数,则程序将暂停一段时间,时间长为该整数值个毫秒单 位,然后继续执行程序,即使用户没有按下任何键。当设置该函数参数为 0 或负 数时,程序将一直等待用户触发按键操作。
cvReleaseImage( &img );
一旦用完加载到内存的图像文件,我们就可以释放为该图像文件所分配的内存 。 我们通过为 cvReleaseImage()函数传递一个类型为 IplImage*的指针参数调用 该函数,用以执行内存释放操作。对 cvReleaseImage()函 数的调用执行完毕后, img 指针将被设置为 NULL。
cvDestroyWindow( &Example1& );
最后,可以销毁显示图像文件的窗口。cvDestroyWindow()函数将关闭窗口,并 同时释放为该窗口所分配的所有内存(包括窗口内部的图像内存缓冲区,该缓冲 区中保存了与 img 指针相关的图像文件像素信息的一个副本)。因为当应用程序 的窗口被关闭时,该应用程序窗口所占用的一切资源都会由操作系统自动释放 , 所以对一些简单程序,不必调用 cvDestroyWindow()或 cvReleaseImage() 函数显
第 23 页 共 110 页
学习 OpenCV.doc
式释放资源。但是,养成习惯每次都调用这些函数显式释放资源总是有 好 处的。
尽管现在已经有了这个可供我们随意把玩的简单程序,但我们并不能就此停滞不 前。我们的下一个任务将要去创建一个几乎与例 2-1 一样非常简单的程序― ― 编写程序读取并播放 AVI 视频文件。完成这个新任务后,需要开始深入思考 一些问
第二个程序―― 播放 AVI 视频
使用 OpenCV 播放视频,几乎与使用它来显示图像一样容易。播放视频时只需要 处理的新问题就是如何循环地顺序读取视频中的每一帧,以及如何从枯燥的电影 视频的读取中退出该循环操作。具体如例 2-2 所示。
例 2-2:一个简单的 OpenC V 程序,用于播放硬盘中的视频文件 #include &highgui.h& int main( int argc, char** argv ) { cvNamedWindow( &Example2&, CV_WINDOW_AUTOSIZE ); CvCapture* capture = cvCreateFileCapture( argv[1] ); IplImage* while(1) { frame = cvQueryFrame( capture ); if( !frame ) cvShowImage( &Example2&, frame ); char c = cvWaitKey(33); if( c == 27 ) } cvReleaseCapture( &capture ); cvDestroyWindow( &Example2& ); } 【18】
这里我们还是通过前面的方法创建一个命名窗口,在“例 2”中,事情变得更加 有趣了。
第 24 页 共 110 页
学习 OpenCV.doc
CvCapture* capture = cvCreateFileCapture( argv[1] );
函数 cvCreateFileCapture()通过参数设置确定要读入的 AVI 文件,返回一个指 向 CvCapture 结构的指针。这个结构包括了所有关于要读入 AVI 文件的信息,其 中包含状态信息。在调用这个函数后,返 回指针所指向的 CvCapture 结构被初始 化到所对应 AVI 文件的开头。
frame = cvQueryFrame( capture );
一旦进入 while(1)循环,我们便开始读入 AVI 文件,cvQueryFrame 的参数为 Cv Capture 结构的指针。用来将下一帧视频文件载入内存(实际是填充或更新 CvCa pture 结构中)。返回一个对应当前帧的指针。与 cvLoadImage 不同的是,cvLoa dImage 为图像分配内存空间,而 cvQueryFrame 使用已经 在 cvCapture 结构中分 配好的内存。这样的话,就没有必要通过 cvReleaseImage()对这个返回的图像 指针进行释放,当 CvCapture 结构被释放后,每一帧图像所 对应的内存空间即会 被释放。
c = cvWaitKey(33); if( c == 27 )
当前帧被显示后,我们会等待 33 ms。[⑩] 如果其间用户触发了一个按键,c 会被设置成这个按键的 ASCII 码,否则,c 会被设置成-1。如果用户触发了 ESC 键(ASCII 27),循环被退出,读入视频停止。否则 33 ms 以后继续执行循环。 需要指出的是,在这 个简单的例子程序中,我们没有 使用任何准确的方法来控制 视频帧率。我们只是 简单的通过 cvWaitKey 来以固定时间间隔载入帧图像,在一 个精度要求更高的程序中,通过从 CvCapture 结构体中 读取实际帧率是一个更好 的方法!
cvReleaseCapture( &capture );
退出循环体(视频文件已经读入结束或者用户触发了 Esc 键)后,我们应该释放为 CvCapture 结构开辟的内存空间,这同 时也会关闭所有打开的 AVI 文件相关的文 件句柄。
视频播放控制
第 25 页 共 110 页
学习 OpenCV.doc
完成了前面的程序以后,现在我们可对其加以改进,进一步探索更多可用的功能。 首先会注意到,例 2-2 所实现的 AVI 播放器无法在视频播放时进行快速拖动。我 们的下一个任务就是通过加入一个滚动条来实现这个功 能。 【19】
HighGUI 工具包不仅提供了我们刚刚使用的一些简单的显示函数,还包括一些图 像和视频控制方法。其中一个经常使用的就是滚动条,滚动条可以使我们方便地 从视频的一帧跳到另外一帧。我们通过调用 cvCreateTrackbar()来创建一个滚 动条,并且通过设置参数确定滚动条所属于的窗 口。为了获得所需的功能,只需 要提供一个回调函数。具体如例 2-3 所示。
例 2-3:用来添加滚动条到基本浏览窗口的程序:拖动滚动条,函数 onTrackSlide()便被调用并被传入滚动条新的状态值 #include &cv.h& #include &highgui.h& int g_slider_position = 0; = NULL;
CvCapture* g_capture
void onTrackbarSlide(int pos) { cvSetC aptureProperty( g_capture, CV_CAP_PROP_POS_FRA MES, pos ); } int main( int argc, char** argv ) { cvNamedWindow( &Example3&, CV_WINDOW_AUTOSIZE ); g_capture = cvCreateFileCapture( argv[1] ); int frames = (int) cvGetCaptureProperty( g_capture, CV_CAP_PROP_FRAME_COUNT );
第 26 页 共 110 页
学习 OpenCV.doc
if( frames!= 0 ) { cvCreateTrackbar( &Position&, &Example3&, &g_slider_position, frames, onTrackbarSlide ); } IplImage* // While loop (as in Example 2) capture & show video ... // Release memory and destroy window ... return(0); }
从本质上说,这种方法是通过添加一个全局变量来 表示滚动条位置并且添加一个 回调函数更新变量以及重新设置视频读入位置。我们通过一个调用来创建滚动条 和确定回调函数。下面让我们看看细节。
int g_slider_position = 0; CvCapture* g_capture = NULL; 【20~21】
首先为滚动条位置定义一个全局变量。由于回调函数需要使用 CvCapture 对象 , 因此我们将它定义为全局变量。为了使我们的程序可读性更强,我们在所有全局 变量名称前面加上 g_。
void onTrackbarSlide(int pos) { cvSetCaptureProperty( g_capture, C V_CAP_PROP_POS_FRAMES,
第 27 页 共 110 页
学习 OpenCV.doc
现在我们定义一个回调函数,使其在滚动条被拖动时调用。滚动条的位置会被作 为一个 32 位整数以参数形式传入。 后面我们会常常看到函数 cvSetCaptureProperty()被调用,同时与之配套的函 数 cvGetCaptureProperty()也经常会被调用。这 些函数允许我们设置(或查询)C vCapture 对象的各种属性。在本程序中我们设置参数 CV_CAP_PROP_POS_ FRAME S(这个参数表示我们以帧数来设置读入位置,如果我们想通过视频长度比例来设 置读入位置,我们可以通过用 AVI_RATIO 代替 FRAMES 来 实现)。最后,我们把新 的滚动条位置作为参数传入。因为 HighGUI 是高度智能化的,它会自动处理一些 问题,比如滚动条对 应位置不是关键帧,它会从前面一 个关键帧开始运行并且快 进到对应帧,而不需要我们来处理这些细节问题。
int frames = (int) cvGetCaptureProperty( g_capture, C V_CAP_PROP_FRAME_COUNT );
正如前面所说,当需要从 CvCapture 结构查询数据时,可使用 cvGetCapturePro perty 函数。在本程序中,我们希望获得视频文件 的总帧数以对滚动条进行设置 (具体实现在后面)。
if( frames!= 0 ) { cvCreateTrackbar( &Position&, &Example3&, &g_slider_position, frames, onTrackbarSlide ); } 【21】
第 28 页 共 110 页
学习 OpenCV.doc
前面的代码用来创建滚动条,借助函数 cvCreateTrackbar(),我们可设置滚动 条的名称并确定滚动条的所属窗口。我们将一个变量绑定到这个滚动条来表示滚 动条的最大值和一个回调函数(不需要回调函数时置为空,当滚动条被拖动时触 发)。仔细分析,你会发现一点:cvGetCaptureProperty()返回的帧数为 0 时, 滚动条不会被创建。这是因为对于 有些编码方式,总的帧数获取不到,在这种情 况下,我们只能直接播放视频文件而看不到滚动条。 值得注意的是,通过 HighGUI 创建的滚动条不像其 他工具提供的滚动条功能这么 全面。当然,也可以使用自己喜欢的其他窗口 开发工具包来代替 HighGUI,但是 HighGUI 可以较快地实现一些基本功能。 最后,我们并没有将 实现滚动条随着视频播放移动功能的代码包含进来,这可作 为一个练习留给读者。
一个简单的变换
很好,现在你已经可以使用 OpenCV 创建自己的视频播放器了,但是我们所关心 的是计算机视觉,所以下面会讨论一些计算机视觉方面的工作。很多基本的视觉 任务包括对视频流的滤波。我们将通过修改前面程序,实现随着视频的播放而对 其中每一帧进行一些简单的运算。 一个简单的变化就是对图像平滑处理,通过对图像数据与高斯或者其他核函数进 行卷积有效的减少图像信息内容。OpenCV 使得这个卷积操作非常容易。我们首 先创建一个窗口“Example4-out”用来显示处理后的图像。然后,在我们调用 cvShowImage()来显示新捕捉的图像以后,我们可以计算和在输出窗口中显示平 滑处理后的图像。具体如例 2-4 所示。
例 2-4:载入一幅图像并进行平滑处理 #include &cv.h& #include &highgui.h& void example2_4( IplImage* image ) // Create some windows to show the input // and output images in. //
第 29 页 共 110 页
学习 OpenCV.doc
cvNamedWindow( &Example4-in& ); cvNamedWindow( &Example4-out& ); // Create a window to show our input image // cvShowImage( &Example4-in&, i mage ); // Create an image to hold the smoothed output // IplImage* out = cvC reateImage( cvGetSize(image), IPL_DEPTH_8U, 3 ); // Do the smoothing // cvSmooth( image, out, CV_GAUSSIAN, 3, 3 ); // Show the smoothed image in the output window // cvShowImage( &Example4-out&, out ); // Be tidy // cvReleaseImage( &out ); // Wait for the user to hit a key, then clean up the windows // cvWaitKey( 0 ); cvDestroyWindow( &Example4-in& ); cvDestroyWindow( &Example4-out& ); } 【22~23】
第 30 页 共 110 页
学习 OpenCV.doc
第一个 cvShowImage()调用跟前面的例子中没有什么不同。在下面的 调用,我们 为另一个图像结构分配空间。前面 依靠 cvCreateFileCapture() 来为新的帧分配 空间。事实上,cvCreateFileCapture()只分配一帧图像的空间,每次调用时覆 盖前面一次的数据(这样每次调用返回的指针是一样的)。在这种情况下,我们想 分配自己的图像结构空间用来存储平滑处理后的图像。第一个参数是一个 CvSiz e 结构,这个结构可以 通过 cvGetSize(image)方便地获得;第一个参数说明了当 前图像结构的大小。第二个参数告诉了我们各通道每个像素点的数据类型,最后 一个参数说明了通道的总数。所以从程序中可以看出,当前图像是 3 个通道(每 个通道 8 位),图像大小同 image。 平滑处理实际上只是对 OpenCV 库函数的一个调用:我们指定输入图像,输出图 像,光滑操作的方法以及平滑处理的一些参数。在本程序中,我们通过使用每个 像素周围 3*3 区域进行高斯平滑处理。事实上,输入、输出图像可以是相同的 , 这将会使我们的程序更加有效,不 过我们为了介绍 cvCreateImage() 函数而没有 这样使用。 现在我们可以在我们新窗口中显示处理后的图像然后释放它:cvReleaseImage() 通过给定一个指向 IplImage*的指针来释放与图像对应的内存空间。 4】
一个复杂一点的变换
前面的工作很棒,我们学会了做一 些更有趣的事情。在例 2-4 中,我们选择了重 新创建一个 IplImage 结构,并且在新建的结构中写入了一个单个变换的结果。 正如前面所提到的,我们可以以这样一种方式来应用某个变换,即用输出来覆盖 输入变量,但这并非总是行得通的。具体说来,有些操作输出的图像与输入图像 相比,大小、深度、通道数目都不一样。通常,我们希望对一些原始图像进 行一 系列操作并且产生一系列变换后的图像。 在这种情况下,介绍 一些封装好的函数是很有用的,这些函数既包含输出图像内 存空间的分配,同时也进行了一些我们感兴趣的 变换。例如,可以考虑对图像进 行缩放比例为 2 的缩放处理[Rosenfeld80]。在 OpenCV 中,我们通过函数 cvPyr
第 31 页 共 110 页
学习 OpenCV.doc
Down()来完成上述功能。这在很多重要的视觉算法中都很有用。我们在例 2-5 中执行这个函数。
例 2-5:使用 cvPyrDown() 创建一幅宽度和高度为输入图像一半尺寸的图像 IplImage* doPyrDown( IplImage* in, int filter = IPL_GA USSIAN_5x5 ){ // Best to make sure input image is divisible by two. // assert( in-&width%2 == 0 && in-&height%2 == 0 ); IplImage* out = cvC reateImage( cvSize( in-&width/2, in-&height/2 ), in-&depth, in-&nChannels ); cvPyrDown( in, out ); return( out ); };
细心的读者会发现,我们分配新的图像空间时是从旧的图像中读取所需的信息 。 在 OpenCV 中,所有的重要数据结构都是以结构体的形式实现,并且以结构体指 针的形式传递。OpenCV 中没有私有数据!现在让我们来看一个与前类似但已加 入 Canny 边界检测的例子[Canny86](见例 2-6)。在这个程序中,边缘检测器产 生了一个与输入图像大小相同但只有一个通道的图 像。
例 2-6:Canny 边缘检测将输出写入一个单通道(灰度级)图像 IplImage* doC anny( IplImage* double in,
【24~25】
lowThresh,
第 32 页 共 110 页
学习 OpenCV.doc
double double ){
h ighThresh, aperture
If(in-&nChannels != 1) return(0); //C anny only handles gray scale images IplImage* out = cvC reateImage( cvSize( cvGetSize( in ), IPL_DEPTH_8U, 1 ); cvC anny( in, out, lowThresh, highThresh, aperture ); return( out ); };
前面的实现使得我们更加容易进行一些连续的变换。例如,如果我们想缩放图像 两次,然后在缩放后的图像中寻找边缘,我们可以很简单的进行操作组合,具体 如 例 2-7 所示。
例 2-7:在一个简单的图像处理流程中进行两次缩放处理与 Canny 边缘检测 IplImage* i mg1 = doPyrDown( in, IPL_GAUSSIAN_5x5 ); IplImage* i mg2 = doPyrDown( img1, IPL_GAUSSIA N_5x5 ); IplImage* i mg3 = doCanny( img2, 10, 100, 3 ); // do whatever with 'img3' // ... cvReleaseImage( &i mg1 ); cvReleaseImage( &i mg2 ); cvReleaseImage( &i mg3 );
第 33 页 共 110 页
学习 OpenCV.doc
注意,对前面的函数进行嵌套调用是一个很不好 的主意,因为那样,内存空间的 释放将会成为一个很困难的问题。如果想偷懒儿,我们可以将下面这行代码加入 我们的每个封装。
cvReleaseImage( &in );
这种“自清理”模式会很干净,但是它同样有一些缺点。比如,如果想对其中 一 个中间步骤的图像进行处理,便无法找到此图像的入口。为了解决前面的问题 , 代码可以简化(如例 2-8 所 示)。 【25】
例 2-8:通过每个独立阶段释放内存来简化例 2-7 中图像处理流程 IplImage* out = doPyrDown( in, IPL_GAUSSIAN_5x5 ); out = doPyrDown( out, IPL_GA USSIAN_5x5 ); out = doCanny( out, 10, 100, 3 ); // do whatever with 'out' // ... cvReleaseImage ( &out );
对于自清理方法的最后一个提醒:在 OpenCV 中,我们必须确认被释放的空间必 须是我们显式分配的。参考前面从 cvQueryFrame()返回的 IplImage*指针,这个 指针指向的结构是 CvCapture 结构的一部分,而 CvCapture 在初始时就已经被初 始化了而且只初始化一次。通过调用 cvReleaseImage()释 放 IplImage*会产生很 多难以处理的问题。前面这个例子主要是想说明,虽然内存垃圾处理在 OpenCV 中很重要,但我们只需要释放自己显式分配的内存空间。
从摄像机读入数据
在计算机世界里, “视觉”这个词的含义非常丰富。在一些情况下,我 们要分析 从其他地方载入的固定图像。在另外一些情况下,我们要分析从磁盘中读入的视 频文件。在更多的情况下,我们想处理从某些摄像设备中实时读入的视频流。
第 34 页 共 110 页
学习 OpenCV.doc
OpenCV―― 更确切地说,OpenCV 中的 HighGUI 模块―― 为我们提供了一种简 单的方式来处理这种情况。这种方 法类似于读取 AVI 文件 ,不同的是,我们调用 的是 cvCreateCameraCapture(),而不是 cvCreateFileCapture()。后面一个函 数参数为摄像设备 ID 而不是文件名。当然,只有存在多个摄像设备时这个参数 才起作用。默认值为-1,代表“随机选择一个” ;自然,它更适合当有且仅有一 个摄像设备的情况(详细内容参考第 4 章)。 函数 cvCreateCameraCapture()同样返回相同的 CvCapture*指针,这使得我们后 面可以使用完全类似于从视频流中获取帧的方法。当然,HighGUI 做很多工作才 使得摄像机图像序列看起来像一个视频文件,但是我们不需要考虑那些问题。当 我们需要处理摄像机图像序列时我们只需要简单地从摄像机获得图像,像视频文 件一样处理。为了 便于开发工作,大多程序实时处理 的程序同样会有视频文件读 入模式,CvCapture 结构的通用性使得这非常容易实现。具体如例 2-9 所示。 【26】
例 2-9:capture 结构初始化后,从视频文件或摄像设备读入图像没有区别 CvCapture* if( argc==1 ) { capture = cvCreateCameraCapture(0); } else { capture = cvCreateFileCapture( argv[1] ); } assert( capture != NULL ); // Rest of program proceeds totally ignorant ...
由此可见,这种处理是很理想的。
写入 AVI 视频文件
在很多程序中,我们想将输入视频流或者捕获的图像序列记录到输出视频流中 , OpenCV 提供了一个简洁的方法来实现这个功能。就像可以创建一个捕获设备以
第 35 页 共 110 页
学习 OpenCV.doc
便每次都能从视频流中提取一帧一样,我们同样可以创建一个写入设备以便逐帧 将视频流写入视频文件。实现这个功能的函数是 cvCreateVideoWriter()。 当输出设备被创建以后,我们可以通过调用 cvWriteFrame()逐帧将视频流写入 文件。写入结束后,我们调用 cvReleaseVideoWriter()来释 放资源。例 2-10 描 述了一个简单的程序。这个程序首 先打开一个视频文件,读取文件内容,将每一 帧图像转换为对数极坐标格式(就像你的眼睛真正看到的,在第 6 章会有详细描 述),最后将转化后的图像序列写入新的视频文件中。
例 2-10:一个完整的程序用来实现读入一个彩色视频文件并以灰度格式输出这个视频文件 // C onvert a video to grayscale // argv[1]: input video file // argv[2]: name of new output file // #include &cv.h& #include &highgui.h& main( int argc, char* argv[] ) { CvCapture* capture = 0; capture = cvCreateFileCapture( argv[1] ); if(!capture){ return -1; } IplImage *bgr_frame=cvQueryFrame(capture);//Init the video read double fps = cvGetCaptureProperty ( capture, CV_CAP_PROP_FPS ); CvSize size = cvSize( (int)cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH), (int)cvGetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT)
第 36 页 共 110 页
学习 OpenCV.doc
); CvVideoWriter *writer = cvCreateVideoWriter( argv[2], CV_FOURCC ('M','J','P','G'), fps, size ); IplImage* logpolar_frame = cvCreateImage( size, IPL_DEPTH_8U, 3 ); while( (bgr_frame=cvQueryFrame(capture)) != NULL ) { cvLogPolar( bgr_frame, logpolar_frame, cvPoint2D32f(bgr_frame-&width/2, bgr_frame-&height/2), 40, CV_INTER_LINEA R+CV_WARP_FILL_OUTLIERS ); cvWriteFrame( writer, logpolar_frame ); } cvReleaseVideoWriter( &writer ); cvReleaseImage( &logpolar_frame ); cvReleaseCapture( &capture ); return(0); } 【27~28】
仔细阅读这个程序,会发现它使用 了一些我们很熟悉的函数。首先,打开一个视 频文件;通过 cvQueryFrame()函数读入视频;然后,我们使用 cvGetCapture-P roperty()来获得视频流的各种重要属性。打开一个视频文件进行此操作,并将
第 37 页 共 110 页
学习 OpenCV.doc
各帧图像转换为对数极坐标格式,将转换后的图像逐帧写入视频文件,直到读入 结束。最后释放各种资源,程序结束。 在对函数 cvCreateVideoWriter()进行调用时,有几个参数是我们必 须 了 解的 。 第一个参数是用来指定新建视频文件的名称。第二个参数是视频压缩的编码格式。 目前有很多流行的编解码器格式,但是无论采用哪种格式,都必须确保自己的电 脑中有这种编解码器(编解码器的安装独立于 OpenCV)。在本程序中,我们选用 比较流行的 MJPG 编码格式;OpenCV 用宏 CV_FOURCC()来指定编码格式,CV_FOU RCC()包含 4 个字符参数。这 4 个字符构成了编解码器的“4 字标记” ,每个编 解码器都有一个这样的标记,Motion JPEG 编码格式的四字标记就是 MJPG,所以 如此指定宏的 4 个字符参数 CV_FOURCC('M','J','P','G')。后面两个参数用来 指定播放的帧率和视频图像的大小。在本程序中,我们将它们设置成原始视频文 件(彩色视频文件)的帧率和图像的大小。
在开始学习第 3 章前,我们需要先总结一下前面的知识,并展望后面要讲的东西。 我们已经知道,OpenCV 为程序开发提供了很多简便易用的接口,可以用来载入 图像、从磁盘或者从摄像设备中捕获视频。我们也知道,OpenCV 库中包含很多 基本的函数用来处理这些图像。但是更多更强大的功能我们还没有看到。它可以 对抽象数据类型进行更多更复杂的处理,这些对解决实际的视觉问题有很大的帮 助。 在后面的几章里,我们会更深入地了解 OpenCV 的一些基础结构并对界面相关函 数和图像数据类型做更详细的分析。我们会系统地讲解一些基本的图像处理操作, 并在后面讲解一些高级的图像变换。最后 ,我们会探索更多专业的应用,如摄像 机标定、跟踪、识别。OpenCV 为这些应用提供 了许多 API 。准备好了吗?让我们 开始 吧! 【29 】
第 38 页 共 110 页
学习 OpenCV.doc
如果还没有在本机上安装 OpenCV,请先下载并安装。浏览目录,在 docs 目录中, 可以找到 index.htm 文件,它链接到库文件的一些主要文档。下面我们进一步了 解一些库文件,Cvcore 包含一些基本数据结构与算法,cv 包含图像处理和视觉 的一些算法,ml 由一些机器学习和聚类算法组成,otherlics/highgui 包含一 些 输入/输出函数。_make 文件夹下包含 OpenCV 的工程文件,程序实例的代码 在 samples 文件夹里。
1. 进入目录…/opencv/_make。在 Windows 环境下,打开解决方案 opencv.sln,在 Linux 环境下,打开对应的 mak
efile,分别建立 debug 和 release 版的库文件。这会花一些时间,但是后面会用到这些库文件和动态链接库。 2. 进入目录…/opencv/samples/c/。建立一个工程(project),导入并编译 lkdemo.c(这是一个运动跟踪程序实例)。安
装上摄像机以后运行程序,选中运行窗口,键入“r”进行跟踪初始化,也可以在视频位置上单击鼠标以添加跟踪点。还可 以通过键入“n”切换为只观察点(而非图像)。再次键入“n”可在“夜间”和“白天”这两个视图之间进行切换。 3. 使用例 2-10 中的视频捕捉和存储方法,结合例 2-5 中的 doPyrDown()创建一个程序,使其从摄像机读入视频数据
并将缩放变换后的彩色图像存入磁盘。 4. 5. 修改练习 3 的代码,结合例 2-1,将变换结果显示在窗口中。 对练习 4 中的代码进行修改,参考例 2-3,给程序加入滚动条,使得用户可以动态调节缩放比例,缩放比例的取
值为 2~8 之间。可以跳过写入磁盘操作,但是必须将变换结果显示在窗口中。
第 3 章 初 探 OpenCV
OpenCV 的基本数据类型
OpenCV 提供了多种基本数据类型。虽然这些数据类型在 C 语言中不是基本类型, 但结构都很简单,可将它们作为原子类型。可以在“? /OpenCV/cxcore/includ e”目录下的 cxtypes.h 文件中查看其详细定义。 在这些数据类型中最简单的就是 CvPoint。CvPoint 是一个包含 integer 类型成 员 x 和 y 的简单结构体。CvPoint 有两个变体类型:CvPoint2D32f 和 CvPoint3D 32f。前者同样有两个成员 x,y,但它们是浮点类型 ;而后者却多了一个浮点类 型的成员 z。 CvSize 类型与 CvPoint 非常相似,但它的数据成员是 integer 类型的 width 和 h eight。如果希望使用浮点类型,则选用 CvSize 的变体类型 CvSize2D32f。
第 39 页 共 110 页
学习 OpenCV.doc
CvRect 类型派生于 CvPoint 和 CvSize,它包含 4 个数据 成员:x ,y,width 和 h eight。(正如你所想的那样,该类型是一个复合类型)。 下一个(但不是最后一个)是包含 4 个整型成员的 CvScalar 类型,当内存不是问 题时,CvScalar 经常用来代替 1,2 或者 3 个实数成员(在这个情况下,不需要 的分量被忽略)。CvScalar 有一个单独的成员 val,它是一个指向 4 个双精度浮 点数数组的指针。 所有这些数据类型具有以其名称来定义的构造函数,例如 cvSize()。(构造函数 通常具有与结构类型一样的名称,只是首字母不大写)。记住,这是 C 而不是 C+ +,所以这些构造函数只是内联函数,它们首先提取参数列表,然后返回被赋予 相关值的结 构。 【31】 各数据类型的内联构造函数被列在表 3-1 中:cvPointXXX(),cvSize(),cvRec t()和 cvScalar()。这些结构都十分有用,因为它们 不仅使代码更容易编写,而 且也更易于阅读。假设要在(5,10)和(20,30)之间画一个白色矩形,只需简单 调用:
cvRectangle( myImg, cvPoint(5,10), cvPoint(20,30), cvScalar(255,255,255) ); 表 3-1:points, size, rectangles 和 calar 三元组的结构
结构 CvPoint CvPoint2D32f CvPoint3D32f CvSize CvRect CvScalar 成员 int x, y float x, y float x, y, z int width, height int x, y, width, height double val[4] 意义 图像中的点 二维空间中的点 三维空间中的点 图像的尺寸 图像的部分区域 RGBA 值
第 40 页 共 110 页
学习 OpenCV.doc
cvScalar 是一个特殊的例子:它有 3 个构造函数。第一个是 cvScalar(),它需 要一个、两个、三个或者四个参数并将这些参数传递 给数组 val[]中的相应元素。 第二个构造函数是 cvRealScalar(),它需要一个参数,它被传递给给 val[0], 而 val[]数组别的值被赋为 0。最后一个有所变化的是 cvScalarAll(),它需要 一个参数并且 val[]中的 4 个元素都会设置为这个参数。
矩阵和图像类型
图 3-1 为我们展示了三种图像的类或结构层次结构。使用 OpenCV 时,会频繁遇 到 IplImage 数据类型,第 2 章已经出现多次。IplImage 是我们用来为通常所说 的“图像”进行编码的基本结构。这些图像可能是灰度,彩色,4 通道的(RGB+a lpha),其中每个通道可以包含任意的整数或浮点数。因此,该类型比常见的、 易于理解的 3 通道 8 位 RGB 图像更通用。 OpenCV 提供了大量实用的图像操作符,包括缩放图像,单通道提取,找出特定 通道最大最小值,两个图像求和,对图像进行阈值操作,等 等。本章我们将仔细 介绍这类操 作。 【32】
图 3-1:虽然 OpenC V 是由 C 语言实现的,但它使用的结构体也是遵循面向对象的思想设计的。实际上,IplImage 由 C vMat 派生,而 CvMat 由 CvArr 派生
在开始探讨图像细节之前,我们需要先了解另一种数据类型 CvMat,OpenCV 的矩 阵结构。虽然 OpenCV 完全由 C 语言实现,但 CvMat 和 IplImage 之间的关系就如 同 C++中的继承关系。实质上,IplImage 可以被视为从 CvMat 中派生的。因此 , 在试图了解复杂的派生类之前,最好先了解基本的类。第三个类 CvArr,可以被
第 41 页 共 110 页
学习 OpenCV.doc
视为一个抽象基类,CvMat 由它派生。在函数原型中,会经常看到 CvArr(更准确 地说,CvArr*),当它出现时,便可以将 CvMat*或 IplImage*传递到程序。
CvMat 矩阵结构
在开始学习矩阵的相关内容之前,我们需要知道两件事情。第一,在 OpenCV 中 没有向量(vector)结构。任何时候需要向量,都只需要一个列矩阵(如果需要一 个转置或者共轭向量,则需要一个行矩阵)。第二,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 通道的简单图像,大多数 的图像操作将分别应用于每一个 通道(除非另有说明)。 实质上,正如例 3-1 所示,CvMat 的结构相当简单 ,(可以自己打开文件? /open cv/cxcore/include/cxtypes.h 查看)。矩阵由宽度(width)、高度(height)、类 型(type)、行数据长度 (step,行的长度用字节表示而不是 整型或者浮点型长度) 和一个指向数据的指针构成(现在我们还不能讨论更多的东西)。可以通过一个指 向 CvMat 的指针访问这些成员,或者对于一些普通元素,使用现成的访问方法 。 例如,为了获得矩阵的大小,可通过调用函数 vGetSize(CvMat*),返回一个 Cv Size 结构,便可以获取任何所需信息,或者通过独立访问高度和宽度,结构为 m atrix-&height 和 matrix-&widt h。 【33~34】
例 3-1:CvMat 结构:矩阵头 typedef struct CvMat {
第 42 页 共 110 页
学习 OpenCV.doc
int* union { uchar* short* int* // for internal use only
float* double* } union { }; union { }; } CvM
此类信息通常被称作矩阵头。很多程序是区分矩阵头和数据 体的,后者是各个 d ata 成员所指向的内存位置。 矩阵有多种创建方法。最常见的方法是用 cvCreateMat(),它由 多个原函数组成, 如 cvCreateMatHeader()和 cvCreateData()。cvCreateMatHeader() 函数创建 Cv Mat 结构,不为数据分配内存,而 cvCreateData()函数只负责数据的内存分配 。 有时,只需要函数 cvCreateMatHeader(),因为已因其他理由分配了 存 储空 间 , 或因为还不准备分配存储空间。第三种方法是用函数 cvCloneMat (CvMat*),它 依据一个现有矩阵创建一个新的矩阵。当这个矩阵不再需要 时,可以调用函数 c
第 43 页 共 110 页
学习 OpenCV.doc
vReleaseMat(CvMat*)释放它。 【34】 例 3-2 概述了这些函数及其密切相关的其他函数。
例 3-2:矩阵的创建和释放 //Create a new rows by cols matrix of type 'type'. // CvMat* cvCreateMat( int rows, int cols, int type ); //Create only matrix header without allocating data // CvMat* cvCreateMatHeader( int rows, int cols, int type ); //Initialize header on existiong CvMat structure // CvMat* cvInitMatHeader( CvMat* mat, int int int rows, cols, type,
void* data = NULL, int ); //Like cvInitMatHeader() but allocates CvMat as well. // CvMat cvMat( int int int rows, cols, type, step = CV_AUTOSTEP
void* data = NULL );
第 44 页 共 110 页
学习 OpenCV.doc
//Allocate a new matrix just like the matrix 'mat'. // CvMat* cvCloneMat( const cvMat* mat ); // Free the matrix 'mat', both header and data. // void cvReleaseMat( CvMat** mat );
与很多 OpenCV 结构类似,有一种构造函数叫 cvMat,它可以创建 CvMat 结构, 但实际上不分配存储空间,仅创建头结构(与 cvInitMatHeader()类似)。这些方 法对于存取到处散放的数据很有作用,可以将矩阵头指向这些数据,实现对这些 数据的打包,并用操作矩阵的函数去处理这些数据,如例 3-3 所示。
例 3-3:用固定数据创建一个 OpenCV 矩阵 //Create an OpenCV Matrix containing some fixed data. // float vals[] = { 0.866025, -0...866025 }; CvM cvInitMatHeader( &rotmat, 2, 2, CV_32FC 1, vals );
一旦我们创建了一个矩阵,便可用它来完成很多事情。最简单的操作就是查询数 组定义和数据访问等。为查询矩阵,我们可以使用函数 cvGetElemType(const C vArr* arr),cvGetDims(const CvArr* arr, int* sizes=NULL) 和 cvGet- DimS ize(const CvArr* arr,int index)。第一个返回一个整型常数,表示存储在数 组里的元素类型(它可以为 CV_8UC1 和 CV_64FC4 等类型)。第二个取出数组以及 一个可选择的整型指针,它返回维数(我们当前的实例是二维,但是在后面我们
第 45 页 共 110 页
学习 OpenCV.doc
将遇到的 N 维矩阵对象)。如果整型指针不为空,它将存储对应数组的高度和宽 度(或者 N 维数)。最后的函数通过一个指示维数的整型 数简单地返回矩阵在那个 维数上矩阵的大 小。 【35~36】
矩阵数据的存取
访问矩阵中的数据有 3 种方法:简单的方法、麻烦的方法和恰当的方法。 简单的方法 从矩阵中得到一个元素的最简单的方法是利用宏 CV_MAT_ELEM()。这个宏(参 见 例 3-4)传入矩阵、待提取的元素的类 的
型、行和列数 4 个参数,返回提取出的元素 值。
例 3-4:利用 CV_MAT_ELEM()宏存取矩阵 CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 ); float element_3_2 = CV_MA T_ELEM( *mat, float, 3, 2 );
更进一步,还有一个与此宏类似的宏,叫 CV_MAT_ELEM_PTR() 。CV_MAT_ELEM_ P TR()(参见例 3-5)传入矩阵、待返回元素的行和列号这 3 个参数,返回指向这个 元素的指针。该宏和 CV_MAT_ELEM()宏的最重要的区 别是后者在指针解引用之前 将其转化成指定的类型。如果需要同时读取数据和设置数据 ,可以直接调用 CV

我要回帖

更多关于 c语言名词解释 的文章

 

随机推荐