概述:
本例程为一个创建内存图像,并将该内存图像利用图像库OpenCV,展示到窗体上。我们将在内存创建一个640x480大小的图像,图像内容是渐变的过渡色,从黑到红。通过本章节例程的学习,我们将掌握一下几个技术点:
- 如何创建一个标准的内存图像数组(BGR分布)
- 如何理解像素格式
- 如何将内存数组绑定到OpenCV图像对象上并展示出来
Step:1:内存数组,创建内存图像
这里我们创建了一个三通道的内存图像,并将内存图像默认置为红色,内存图像的像素格式选取的BGR格式,也就是说,在这幅内存图像中,所有的像素都是按照BGR依次摆开的,所以该内存数组的大小为 width * height * 3。创建好之后将该数组遍历一遍,并将B通道像素置为255,G通道和R通道像素置为0。
unsigned char* CreateBGRMemoryImage(int nWidth, int nHeight)
{
//我们在选取的BGR像素格式,所以一共是三个通道
int nImgChannel = 3;
//用来作为数组访问的索引
int nPixelIndex = 0;
//创建了内存数组,大小为width * height * 3
unsigned char* pMemoryImage = new unsigned char[nWidth * nHeight * nImgChannel];
//对内存数组进行初始化,将R通道置为255
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
pMemoryImage[nPixelIndex++] = 0; // B channel
pMemoryImage[nPixelIndex++] = 0; // G channel
pMemoryImage[nPixelIndex++] = 255; // R channel
}
//将数组地址返回
return pMemoryImage;
}
Step:2:像素格式,图像数据在内存里的分布方式
像素格式是图像数组里数据的分布格式,我们经常遇到的有BGR,RGB,BGRA,YUV等
正如我们在第一步创建内存数组的时候,不同的像素格式,内存数组就有不同的含义,BGR代表着像素 分布是第一个像素为B值,第二个像素为G值,第三个像素为R值,RGB则代表的恰恰相反,在RGB像素格式的内存图像中,第一个像素值为R值,第二个像素格式为G值,第三个像素格式为B值,BGRA则是多了一个通道ALPHA,YUV为另一种图像格式,和RGB颜色模型并列的YUV颜色模型。第一个像素代表Y值,第二个像素代表U值,低三个像素代表V值。
BGR和YUV也有转化公式,我们可以从网上得到。
另外我们也经常会在一些图像库中遇到这样的像素格式,以OpenCV举例,OpenCV中有一个像素格式转换CV_BGR2BGR565,BGR我们知道,是BGR分布的数组,BGR565是更为具体的内存数组格式,我们简单地为每个像素用一个8字节的unsigned char来存储,一个像素占据的内存大小为8 * 3 = 24个字节,我们的BGR可以称之为BGR888,那样就很清楚了BGR565的意思就是,对我们的BGR888做了一个优化,B通道像素保留了前5位,G通道像素保留了前6位,R通道像素保留了前5位,然后将这些像素重新进行排列。在很多图像硬件开发过程中会遇到这个问题,以此类推即可。
//对内存数组进行初始化,将R通道置为255
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
//初始化B通道像素值
pMemoryImage[nPixelIndex++] = 0; // B channel
//初始化G通道像素值
pMemoryImage[nPixelIndex++] = 0; // G channel
//初始化R通道像素值
pMemoryImage[nPixelIndex++] = 255; // R channel
}
Step:3:窗体展示,将内存数据利用图像库展示到窗体
OpenCV是一个基于BSD许可(开源)发行的跨平台计算机视觉库,里面包含了很多图像的基础操作,和平台扩展,我们借助OpenCV的窗体库,来将我们的内存图像进行展示,在这个过程中,我们也将学会如何将我们的内存图像融合到其他框架里。图像框架有很多种,但掌握核心的算法,会让我们对图形图像理解更深刻,也更容易适应了纷繁复杂的要求,帮助读者达到在工作学习的过程中借助框架而不依赖框架,是本教程的意义所在。
言归正传,OpenCV里对图像的数据结构为IplImage,在最新的版本中(4.x),取消掉了C语言形式的数据结构,在该系列教程中我们依旧以此为例阐述,我们将创建的内存绑定到这个数据结构里的时候,就可以利用OpenCV的窗体库,将图像展示出来。可以这样理解
如果我们想借助OpenCV丰富的图像函数来进行图像操作,现在也可以进行了,因为,我们的内存数组已经转化为一个OpenCV图像对象了。具体过程为:
bool ShowBGRImage(unsigned char* pImage, int nWidth, int nHeight)
{
//创建3通道的OpenCV图像对象
IplImage* pSrcImage = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 3);
//获得图像对象的图像数组地址
unsigned char* pImageData = (unsigned char*)pSrcImage->imageData;
//赋值索引
int nImageIndex = 0;
//将我们的内存图像赋值到OpenCV的IplImage对象中
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
//OpenCV里,图像组是逐行存储的,其每一行的大小用widthStep记录,我们的每一行必须赋值到对应的OpenCV行中,在此得到OpenCV图像数组的索引
int nCurPixelIndex = nYIndex * pSrcImage->widthStep + nXIndex * 3;
//赋值B通道像素
pImageData[nCurPixelIndex++] = pImage[nImageIndex++];
//复制G通道像素
pImageData[nCurPixelIndex++] = pImage[nImageIndex++];
//赋值R通道像素
pImageData[nCurPixelIndex++] = pImage[nImageIndex++];
}
//对OpenCV对象进行窗体展示
cvShowImage(windowName, pSrcImage);
//释放我们创建的OpenCV对象
cvReleaseImage(&pSrcImage);
return true;
}
3.工程示例
/***************************************************************************
*
* 本例程是一個如何操控内存图像的示例,并在章节1的基础上将过程动态的显示出来
*
*****************************************************************************/
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include <iostream>
#pragma comment(lib,"opencv_core341d.lib")
#pragma comment(lib,"opencv_highgui341d.lib")
using namespace std;
/**窗口Name**/
const char* windowName = "BGR Window ... Press ESC Quit";
/************************************
*
* 創建BGR內存圖像
*
************************************/
unsigned char* CreateBGRMemoryImage(int nWidth, int nHeight);
/************************************
*
* 窗體顯示BGR內存圖像
*
************************************/
bool ShowBGRImage(unsigned char* pImage, int nWidth, int nHeight);
int main(int argc, char ** argv)
{
//设置显示窗体的size
int nIMGWidth = 640;
int nIMGHeight = 480;
double nPixelColorAdd = 255.0 / 640;
//create memory image buffer
unsigned char* theMemoryImage = CreateBGRMemoryImage(nIMGWidth, nIMGHeight);
ShowBGRImage(theMemoryImage, nIMGWidth, nIMGHeight);
cvWaitKey(0);
delete[] theMemoryImage;
return 0;
}
unsigned char* CreateBGRMemoryImage(int nWidth, int nHeight)
{
int nImgChannel = 3;
int nPixelIndex = 0;
unsigned char* pMemoryImage = new unsigned char[nWidth * nHeight * nImgChannel];
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
pMemoryImage[nPixelIndex++] = 0; // B channel
pMemoryImage[nPixelIndex++] = 0; // G channel
pMemoryImage[nPixelIndex++] = 255; // R channel
}
return pMemoryImage;
}
bool ShowBGRImage(unsigned char* pImage, int nWidth, int nHeight)
{
IplImage* pSrcImage = cvCreateImage(cvSize(nWidth, nHeight), IPL_DEPTH_8U, 3);
unsigned char* pImageData = (unsigned char*)pSrcImage->imageData;
int nImageIndex = 0;
//fill memory img to cvbuffer
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
int nCurPixelIndex = nYIndex * pSrcImage->widthStep + nXIndex * 3;
pImageData[nCurPixelIndex++] = pImage[nImageIndex++];
pImageData[nCurPixelIndex++] = pImage[nImageIndex++];
pImageData[nCurPixelIndex++] = pImage[nImageIndex++];
}
cvShowImage(windowName, pSrcImage);
cvReleaseImage(&pSrcImage);
return true;
}
4.效果图
4.1初始化为红色
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
pMemoryImage[nPixelIndex++] = 0; // B channel
pMemoryImage[nPixelIndex++] = 0; // G channel
pMemoryImage[nPixelIndex++] = 255; // R channel
}
4.2初始化为蓝色
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
pMemoryImage[nPixelIndex++] = 255; // B channel
pMemoryImage[nPixelIndex++] = 0; // G channel
pMemoryImage[nPixelIndex++] = 0; // R channel
}
4.3初始化为护眼色
for (int nYIndex = 0; nYIndex < nHeight; nYIndex++)
for (int nXIndex = 0; nXIndex < nWidth; nXIndex++)
{
pMemoryImage[nPixelIndex++] = 128; // B channel
pMemoryImage[nPixelIndex++] = 128; // G channel
pMemoryImage[nPixelIndex++] = 0; // R channel
}
结语:
在本章节中,我们学会了如何创建一个BGR图像格式的内存数组,并利用OpenCV图像库,将我们的图像数据展示到窗体