`
grunt1223
  • 浏览: 423232 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

【翻译】Seeing With OpenCV - Part 1: Introduction to OpenCV

阅读更多
本文翻译自Robin Hewitt的《Seeing With OpenCV - A Five-Part Series》。

正如标题所表达的那样,通过连载的五篇文章,我们将通过step by step的方式,实现一个人脸识别的程序,即从原始图片中检测脸部,并通过某种算法从样本库中筛选近似的肖像加以匹配,这是相当有用&有趣的应用。

这是本系列的第一章,主要介绍OpenCV及其一些简单用法。

1、OpenCV简介
OpenCV是一款免费、开源、使用C&C++编写的计算机视觉库,可以从http://sourceforge.net/projects/opencvlibrary
下载获得。

Intel在1999年发布了OpenCV的第一个版本,当时它需要Intel的图形处理包。现在,这个限制已经被去除,你可以像使用任何一个标准库一样使用OpenCV。

OpenCV被设计为跨平台,同时支持Windows、Linux以及MacOSX。有一个例外,CVCAM(负责摄像头处理)被设计为平台相关,这点后面会讨论到。

2、特点
OpenCV具有强大的图形处理功能,庞大的API可能会让初学者产生畏惧感。因此,作为初学者,你只需要了解其中最常用的部分。

通用计算机视觉以及图形处理算法(中、低层API)
使用这些接口,无需复杂的编写图像算法就可以实现一些常用的图像处理功能,包括边缘&直线&角的检测、椭圆拟合、多分辨率的图形金字塔算法、模板匹配,各种变换(傅里叶、离散余弦、距离)等等。

高层计算机视觉模块
OpenCV包含了许多高层的功能,例如脸部检测&识别&跟踪等

人工智能与机器学习方法
计算机视觉的问题往往会涉及到一些机器学习以及人工智能的方法,这些都被包装在OpenCV的机器学习包里面

图像采样与视角变换
OpenCV提供了图像子区域提取、随机采样、图形缩放、包装、旋转以及透视变换等功能

二值化图像的创建和处理
OpenCV提供的二值化图像的创建和分析功能,在监控、形状检测以及物件计数等领域往往很有用

3D计算与处理
这些方法可以用于3D建模和定位,以及摄像机的多视角处理

用于图像处理、计算机视觉的数学库
OpenCV提供了大量常用的数学运算库,包括线性代数、统计学以及几何运算等等

作图
这些接口可以使你很方便地再图片上写字或者作画。可以通过它实现一些简单的水印功能,或者辅助其它更高阶的功能,例如在进行物体识别的同时,可以使用该接口标记检测出来的位置和区域。

GUI
OpenCV封装了许多跨平台的GUI接口作为API,你可以实现自己的应用,操作键盘、鼠标、滑块等控件。

数据结构与算法
使用这些接口,你可以有效地存储、查询、保存和操作一些常用的数据结构,比如链表、几何、树、图等

数据持久化
这些接口提供了将数据保存到本地以便日后读取的功能

下图展示了OpenCV的一些应用场景,包括脸部识别、轮廓检测以及边缘检测。



3、模块结构
OpenCV包含以下几个模块:

CXCORE包含了一些基本的数据类型的定义。例如,一些常见的图、点、矩形等数据结构定义在cxtypes.h中。CXCORE同时也包含含线性代数和统计等模块,以及持久化函数、错误句柄处理、以及画图等功能。

CV包含了图像处理以及摄像机校正、几何计算等方法。

CVAUX包含了一些实验型的方法,比如脸部识别等

ML主要是一些机器学习的方法

HighGUI包含了一些基本的IO操作以及一些跨平台的界面调用接口

CVCAM包含了一些视频处理的接口,使得你不必亲自去使用DirectX

4、OpenCV编程基础
关于头文件与库文件的引用
本文所介绍的大部分程序都需要引用cv.h和highgui.h。脸部识别的程序还需要添加cvaux.h。这些头文件自身引用了其余的大多数其它头文件。

你的连接器也需要添加cxcore.lib、cv.lib以及highgui.lib。对于脸部识别的程序,还需要添加cvaux.lib,它们都在OpenCV的lib目录下。

图片的读写
下面的实例代码说明了如何读取图片文件,并采用不同的压缩格式(jpg -> png)保存为另一个文件。
#include <stdio.h>

int main(int argc, char** argv)
{
  IplImage * pInpImg = 0;

  // Load an image from file
  pInpImg = cvLoadImage("my_image.jpg", CV_LOAD_IMAGE_UNCHANGED);
  if(!pInpImg)
  {
    fprintf(stderr, "failed to load input image\n");
    return -1;
  }

  // Write the image to a file with a different name,
  // using a different image format -- .png instead of .jpg
  if( !cvSaveImage("my_image_copy.png", pInpImg) )
  {
    fprintf(stderr, "failed to write image file\n");
  }

  // Remember to free image memory after using it!
  cvReleaseImage(&pInpImg);

  return 0;
}


当读取图片时,仅需要调用cvLoadImage方法,并且传递给他文件名作为参数即可(如第十四行所述)。OpenCV支持常见的大多数的图片格式,包括JPEG、PNG和BMP(目前还不支持GIF)。你无须关心图片具体是按照何种格式压缩保存的,cvLoadImage会在读取图片头文件信息的时候决定采用哪一种格式的解码器。

可以通过cvSaveImage方法将内存中的图片保存为文件。保存文件时,采用何种压缩格式,是由传入的文件的扩展名决定的。比如,传入参数为"XXX.png",就以PNG的格式来存储。

在调用cvLoadImage之后,使用完图片资源之后务必记得调用cvReleaseImage来释放内存。

cvLoadImage与cvSaveImage都被封装在HighGUI模块中。

色彩空间的转换
下面的例子说明了如何将一幅图片转化为灰度图。OpenCV内置了许多色彩空间模型方便自由地转换,包括RGB、HSV、YCrCb已经CIELAB等等。如果你对色彩模型感兴趣,推荐看SERVO Magazine的《The World of Color》。

// ConvertToGray.c
// Example showing how to convert an image from color
// to grayscale

#include "stdio.h"
#include "string.h"
#include "cv.h"
#include "highgui.h"

int main(int argc, char** argv)
{
  IplImage * pRGBImg  = 0;
  IplImage * pGrayImg = 0;

  // Load the RGB image from file
  pRGBImg = cvLoadImage("my_image.jpg", CV_LOAD_IMAGE_UNCHANGED);
  if(!pRGBImg)
  {
    fprintf(stderr, "failed to load input image\n");
    return -1;
  }

  // Allocate the grayscale image
  pGrayImg = cvCreateImage
    ( cvSize(pRGBImg->width, pRGBImg->height), pRGBImg->depth, 1 );

  // Convert it to grayscale
  cvCvtColor(pRGBImg, pGrayImg, CV_RGB2GRAY);

  // Write the grayscale image to a file
  if( !cvSaveImage("my_image_gray.jpg", pGrayImg) )
  {
    fprintf(stderr, "failed to write image file\n");
  }

  // Free image memory
  cvReleaseImage(&pRGBImg);
  cvReleaseImage(&pGrayImg);

  return 0;
}

注意,具体执行色彩转换的函数——cvCvtColor,其参数列表包含两个图片指针类型,第一个参数,pRGBImg,代表源图像。第二个参数,pGrayImg,代表目标图像。

注意25行,我们使用cvCreateImage创建了一个与原始图像相同尺寸,但为初始化像素的数据结构,这类操作在今后的OpenCV编程中会很常见。

OpenCV图片的数据结构

OpenCV使用标准的C语言结构体来。其中,IPL是Image Processing Library的缩写。IplImage是在CXCOR中定义的,除了原始的像素数据之外,它还定义了一些元数据(描述字段),统一称为图片的“头文件”。它包括:

  • width —— 图片宽度,以像素为单位
  • height —— 图片高度,以像素为单位
  • depth —— 图片位深,即每通道每像素所包含的位的数目。比如,depth为IPL_DEPTH_8U,则表示单个通道的每个像素为8位无符号类型,即0-255
  • nChannels —— 通道数量,一般为1-4. 每个通道包含一种像素数据类型。比如RGB图像包含三个通道,因为每个像素都存储了红、绿、蓝三种颜色信息;而灰度图像只包含一个通道,即像素的亮度。

操作像素值
事实上,使用OpenCV的大部分应用,都不需要去直接操作原始的像素数据,比如脸部检测、追踪、识别等;它们只需要使用图像指针以及其它的一些高层的数据结构。OpenCV的许多函数封装了像素级别的计算。尽管如此,你还是会有直接操作原始像素数据的场景,比如当你需要自己写一些图形算法的时候。下面介绍两种操作像素的方法:

1、简单的像素操作
最简单的读取单个像素的方法时采用cvGet2D方法:

该函数具有三个参数:一个指向数据容器的指针(CVArr*)以及代表了位置坐标(第几行第几列)的数组。数据容器可以使IplImage结构体。最顶上一行的像素的row=0,最低部一行的row=height-1。

cvGet2D返回一个C结构体,CvScalar,其定义如下:
typedef struct CvScalar
{
    double val[4];
}
CvScalar;


每个通道的像素值被保存在val[i]中。对于灰度图像,val[0]代表了像素的亮度值,而其它三个值则被设为0,。对于三通道的BGR图片,blue=val[0],green=val[1],red=val[2]。

另一个函数,cvSet2D方法允许你修改像素值,它的定义如下:
void cvSet2D(CvArr*, int row, int col, CvScalar);


2、快速的像素操作
尽管vGet2D()以及cvSet2D()十分方便使用,但是对于批量存取大量像素点来说,它们的性能很糟糕。此种情况下,你需要直接从原生的数据缓存,即IplImage.imageData中读取像素值。

缓存中的图像数据以一维数组的形式存放,以行优先的顺序,即:先顺序存放第一行的所有数据,再存放第二行的所有数据,以此类推。

出于性能考虑,所有的像素数据都经过对齐,并且在需要的时候补0。IplImage的第二个字段,IplImage.widthStep,代表相邻两行数据之间的bit数,也就是说,第i行的数据指针指向
IplImage.imageData + i*IplImage.widthStep处。

IplImage.imageData被定义为char*类型,因此必要时你需要转换数据类型,比如,通常来说,你的图像数据是unsigned bytes,你在使用它时往往需要将其转化为unsigned char*。

如果你在操作的是位深等于8的灰度图像,可以通过如下方式操作pixel[row][col]:
pixel[row][col] =
    (uchar*)(pImg->imageData + row*pImg->widthStep + col);


最后,如果遇到图片的位深大于8bit的情况(比如,IPL_DEPTH_32S),你需要将其转化为多字节的数据类型,并且将其width step乘以字节数作为其实际的位深(比如,对于IPL_DEPTH_32S来说,你需要将buffer转化为int*,并且将使用4倍的width step。)。尽管如此,实际情况下,很少会遇到需要直接操作多字节像素值的场景。

5、下一章预告
在Part 2,我将会介绍如何使用OpenCV来实现脸部检测,并且详细讲解其背后的算法,感谢你的阅读。
  • 大小: 165.9 KB
2
5
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics