近日做了些与OpenCV、图像处理相关的工程,两个周的时间收获了挺多,故将涉及到的内容进行整理,方便以后查阅,共同学习。 本文主要介绍OpenCV使用、图像格式相关的内容。
Catalog
OpenCV2.4.10 + Win10 VS2015的安装配置
工具
- OpenCV 下载地址:http://opencv.org/downloads.html
- Visual Studio 2015
环境变量配置
安装OpenCV并解压缩;
配置环境变量:
- 系统变量PATH:e.g.
E:\opencv\build\x86\vc12\bin
- 用户变量:
- 添加opencv变量:e.g.
E:\opencv\build
- 补全PATH变量:e.g.
E:\opencv\build\x86\vc12\bin
- 注:不管操作系统是32位或64位,建议上述目录均选择32位,因为一般都是32位的编译方式,若选择x64路径则可能会出错。
- 添加opencv变量:e.g.
在VS中新建项目
选择Visual C++中Win32 Console Application(Win32 控制台应用程序)进行创建;
进入Win32应用程序向导:
- 应用程序类型:选择控制台应用程序
- 附加选项:空项目、预编译头
工程目录的配置(Debug)
在窗口右侧上栏找到Property Manager(属性管理器)
,双击Debug | Win32
:
- 按顺序找到 Common Properties - VC++ Directories - Include Directories,添加
E:\opencv\build\include
,E:\opencv\build\include\opencv
,E:\opencv\build\include\opencv2
。 - 按顺序找到 Common Properties - VC++ Directories - Library Directories,添加
E:\opencv\build\x86\vc12\lib
。 - 按顺序找到 Common Properties - Linker - Input - Addtional Dependencies,添加
E:\opencv\build\x86\vc12\lib
中的所有后缀带d
的文件名。示例:
opencv_calib3d2410d.lib
opencv_contrib2410d.lib
opencv_core2410d.lib
......
工程目录的配置(Release)
- 类似地,双击
Release | Win32
,按照顺序找到 Common Properties - Linker - Input - Addtional Dependencies,添加E:\opencv\build\x86\vc12\lib
中的所有后缀 不带d
的文件名。
测试代码
#include <cv.h>
#include <highgui.h>
using namespace cv;
using namespace std;
int main()
{
IplImage * test;
test = cvLoadImage("D:\\Sample_8.bmp");//路径,注意加双斜杠转义
cvNamedWindow("test_demo", 1);
cvShowImage("test_demo", test);
cvWaitKey(0);
cvDestroyWindow("test_demo");
cvReleaseImage(&test);
system("pause");
return 0;
}
OpenCV基本用法
基本操作
OpenCV通过结构体IplImage
存储图片的信息,通过指向IplImage
的指针对图片进行操作。
typedef struct _IplImage
{
int nSize; /* sizeof(IplImage) */
int ID; /* version (=0)*/
int nChannels; /* Most of OpenCV functions support 1,2,3 or 4 channels */
int alphaChannel; /* Ignored by OpenCV */
int depth; /* Pixel depth in bits: IPL_DEPTH_8U, IPL_DEPTH_8S, IPL_DEPTH_16S,
IPL_DEPTH_32S, IPL_DEPTH_32F and IPL_DEPTH_64F are supported. */
char colorModel[4]; /* Ignored by OpenCV */
char channelSeq[4]; /* ditto */
int dataOrder; /* 0 - interleaved color channels, 1 - separate color channels.
cvCreateImage can only create interleaved images */
int origin; /* 0 - top-left origin,
1 - bottom-left origin (Windows bitmaps style). */
int align; /* Alignment of image rows (4 or 8).
OpenCV ignores it and uses widthStep instead. */
int width; /* Image width in pixels. */
int height; /* Image height in pixels. */
struct _IplROI *roi; /* Image ROI. If NULL, the whole image is selected. */
struct _IplImage *maskROI; /* Must be NULL. */
void *imageId; /* " " */
struct _IplTileInfo *tileInfo; /* " " */
int imageSize; /* Image data size in bytes
(==image->height*image->widthStep
in case of interleaved data)*/
char *imageData; /* Pointer to aligned image data. */
int widthStep; /* Size of aligned image row in bytes. */
int BorderMode[4]; /* Ignored by OpenCV. */
int BorderConst[4]; /* Ditto. */
char *imageDataOrigin; /* Pointer to very origin of image data
(not necessarily aligned) -
needed for correct deallocation */
}IplImage;
其中,比较重要的成员为:
int nChannels
:图像通道数,如RGB为3,RGBA为4,YUV为1。int depth
:图像深度,即各像素的数值类型,如IPL_DEPTH_8U为8bits unsigned char,其余支持类型见上述代码说明。int width
:图像宽度。int height
:图像高度。int imageSize
:图像占用字节数,即height * widthStep
。char *imageData
:指向图像数据的char
型指针。int widthStep
:图像宽度步长。需要特别注意的值,因为图像每行需要内存对齐,所以width
和widthStep
是不同的。比如:width = 311, widthStep = 312
,我自己测试在x86上widthStep必须是4的倍数。此概念对于操作图像具体像素值是非常重要的。
读取图片
function: CVAPI(IplImage*) cvLoadImage( const char* filename, int iscolor CV_DEFAULT(CV_LOAD_IMAGE_COLOR));
example: IplImage *test_ori = cvLoadImage(argv[1], 1);
创建图片
function: CVAPI(IplImage*) cvCreateImage( CvSize size, int depth, int channels );
example: IplImage *rec_ori = cvCreateImage(cvSize(cvwidth, cvheight), IPL_DEPTH_8U, 3);
显示图片
function: CVAPI(void) cvShowImage( const char* name, const CvArr* image );
example: cvShowImage("recover", rec_ori);
转换图片色彩空间
function: CVAPI(void) cvCvtColor( const CvArr* src, CvArr* dst, int code );
example: cvCvtColor(test_ori, cvt_yuv422, CV_BGR2YUV);
注:cvCvtColor函数虽然提供了多种自带转换,但不提供BGR–>YUV422I(YUY2、YUNV、YUYV、V422)的转换,反而提供YUV422I–>BGR的转换。此处,YUV422I是Android常用的图片格式,但如若进行图像处理,则一般需要转换成RGB。
分通道显示图像
function: void cvSplit(const CvArr* src,CvArr *dst0,CvArr *dst1, CvArr *dst2, CvArr *dst3);
example:
for (int i = 0; i < test_ori->nChannels; ++i) {
imgChannel_[i] = cvCreateImage(cvGetSize(test_ori), test_ori->depth, 1);
}
cvSplit(test_ori, imgChannel_[0], imgChannel_[1], imgChannel_[2], 0);
cvShowImage("B_", imgChannel_[0]);
cvShowImage("G_", imgChannel_[1]);
cvShowImage("R_", imgChannel_[2]);
注:通常情况下,通道小于4,则可以填0或NULL以补全。显示每个通道时,也是灰色图像,如果要以蓝、绿、红色展示,有其他方法,但我个人感觉并不需要,因为颜色只是一种直观的判断方法,但是具体还是其中的数值。
其他
图像数值显示:一般的RGB、YUV或者灰度图像基本都是以8bits unsigned char的形式存储,范围在0~255之间。在做图像转换、恢复时,经常需要检查具体图像数据,因此建议以十六进制显示为佳。显示方式如下:
printf("0x%.2x", x1); // x1为某数值
RGB/YUV色彩空间
一般的图像传输流程(以YUV传输为例)
发送端: 彩色图像 –> 分色后放大校正得到RGB图像 –> 矩阵变换 –> 得到YUV –> 将三信号分别编码发送
接收端: 解码得到YUV –> 转换YUV到RGB —> 进一步处理
RGB与YUV的相互转换
RGB的原理是三者的组合可以构成任何颜色,用三个0~255的数构建一个像素的信息。然而,YUV更加符合人类视觉的习惯。
人类大脑最先感知的是亮度。
依据这个理论,结合人眼对于不同颜色的灵敏度相异,采用YUV的模型也能够更好反映色彩。三个值有不同的含义:
- Y代表亮度,是个正数,在0~255之间,0为黑,255为白;
- U(Cr)有正有负,正数时代表红色,负数时代表绿色;
- V(Cb)有正有负,正数时代表蓝色,负数时代表黄色。
明白了RGB和YUV的含义,接下来列出二者互相转化的公式。可以看出,二者是线性转换,1个RGB向量对应1个YUV向量,使得亮度、色度、饱和度信息分离。
注:上述转换标准是SDTV with BT.601(ITU-R Recommendation BT.601)所规范的,是较为被普遍使用的一种,当然还有其他规范。依旧是线性变换,但区别在于具体参数不同。
数值近似
在众多的SIMD中,受限于计算性能要求,并不直接采用上述浮点数计算,而是用整数替代,2次幂的乘法、除法则用左移\(\ll\)、右移\(\gg\)来实现。
RGB –> YUV
YUV –> RGB
注:\(clamp\left ( \right ) \)指将括号内数值整形至0~255之间。
需要注意的几点
- RGB –> YUV 转换无需整形至0~255,而 YUV –> RGB 需要;
- RGB –> YUV 转换时,U、V最后加上128目的是使其处于0~255之间,便于计算机采用8比特uchar计数,否则会有正有负。Y加16也是类似的考虑。
- 可观察到右移8位前,都会加128,这是考虑了四舍五入。因为右移8位即除以\( 2^{8} = 256 \),而加上\( 2^{7} = 128 \),故进行了四舍五入。这是一种惯用的ground方法,应该从二进制的视角来看。
常见图像格式
YUV
YUV基本概念在上节已述,但实际上人眼对于3个8比特uchar表示1个像素的图片的感知,与略微降低采样频率后的图像相差无几。因此为了精简数据,有不同程度的采样比。若做图像色彩空间相互转换,实质肯定是有损的,但是依旧由于人眼对于这种细微差别的不敏感,采样后的图像经过转换回RGB,还是很不错的。
- YUV444 指平均4个像素采样4个Y、4个U、4个V;
- YUV422 指平均4个像素采样4个Y、2个U、2个V;
- YUV420 指平均4个像素采样4个Y、1个U、1个V;
下面以YUV422I为例,说明采样排列格式。这是一种在Android上经常遇到的图像格式。
YUV422I是YUV422采样中的一种格式,又被称为YUY2、YUNV、YUYV、V422,具体命名及格式请见YUV pixel formats。不同的YUV422格式采样方式都是4:2:2,但是排列格式相互不同。比如YUV422I在编码时,在图像的每一行的每两个像素的维度上进行,这两个像素被称为MacroPixel。采样时保留这两个像素的亮度信号,记为Y1、Y2,但只保留第一个像素的U、V当做这个MacroPixel的U、V,记为U1、V1。排列时以Y1 U1 Y2 V1
为准。请参见下图。