Hi3519V101 {MOD}彩空间转换总结(一)
2017年12月15日 22:04:21
阅读数:531
目录
前言
虽然只是做了点海思的应用层开发,但是对于各种颜 {MOD}格式也有一定的了解。因为海思本身是
YUV420sp
格式的数据。在开发的过程中,要把
YUV420sp
的数据转换成
rgb
数据,当然也是要用到
opencv
。踩了很多坑,但最终成功的把
YUV420sp
的数据转换成了
rgb
数据。
学习!分享!感谢!
保存灰度图像
当从海思中使用
HI_MPI_VI_GetFrame
获取图像时,获取的是
yuv420sp
格式的图像。假设
Y
分量:宽
Width
,高
Height
。那么,
UV
分量就是
(Width/2)*(Height/2)*2
,所以所占的字节数就是
3*Width*Height/2
。
YUV
格式参见
图文详解YUV420数据格式
最开始不懂图像格式的时候,使用的
3518e保存图像到opencv IplImage结构体中的代码,成功的保存了一张灰度图像。
-
opencv
中创建
IplImage
格式的方法:
这里需要用到两个函数
cvCreateImageHeader
和
cvCreateImage
。具体可以参见
浅谈cvCreateImageHeader,cvCreateImage和
cvCreateImageHeader导致内存不足的问题。
其中,需要注意的就是
cvCreateImage
创建了图像头并且为图像分配了数据。而如果我们有了图像数据,就只需要创建图像头
cvCreateImageHeader
,但是我们释放的注意要把图像头所占的内存释放掉,同时要把对应的图像数据所占内存释放掉,否则就会造成内存不足。
- 整理
3518e保存图像到opencv IplImage结构体中
9crk给的代码。
虽然代码量很少,但是这几句代码也是我切入
openCV
的起始。毕竟所有的图像处理都是要先获取到图像资源的,虽然现在仍然对图像处理的知识一窍不通。但是也算有图像有了点皮毛的了解,知识只能慢慢学,慢慢实践,否则理解不深刻,过段时间又忘记了!
#define WIDTH 1920
#define HEIGHT 1080
int main()
{
char data[WIDTH*HEIGHT];
int len; // Y分量的数据大小
vi_getFrameInit();
IplImage *img = cvCreateImageHeader(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1); // IPL_DEPTH_8U表示8位无符号整数,也就是256 {MOD},1表示单通道
cvSetData(img, data, WIDTH);
len = vi_getYUV(data);
cvSaveImage("myImage.jpg", img);
cvReleaseImageHeader(&img); // 注意:&
}
int vi_getFrameInit()
{
HI_U32 u32Depth = 8;
VI_CHN ViChn = 0;
HI_S32 s32ret;
s32ret = HI_MPI_VI_SetFrameDepth(ViChn, u32Depth);
}
int vi_getYUV(char* yData)
{
HI_S32 s32ret;
VIDEO_FRAME_INFO_S stFrame;
HI_VOID* yPtr;
int len;
s32ret = HI_MPI_VI_GetFrame(ViChn, &stFrame);
if(s32ret != 0){
return 0;
}
// 获取视频帧的Y分量大小
len = stFrame.stVFrame.u32Width* stFrame.stVFrame.u32Height;
yPtr= HI_MPI_SYS_Mmap(stFrame.stVFrame.u32PhyAddr[0], len);
memcpy(yData, yPtr, len); // 似乎无法直接对视频帧直接进行处理,只能把数据拷贝出来才能够进行处理,目前遇到似乎都是如此。
HI_MPI_SYS_Munmap(yPtr, len);
(void)HI_MPI_VI_ReleaseFrame(ViChn, &stFrame);
return len;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
保存彩 {MOD}图像
上面的代码给出了通过
opencv
获取灰度图像的方法。但是,在实际的开发中,我们可能需要获得的是
rgb
格式的彩 {MOD}图片,这时候我们就需要了解
YUV420sp
的格式。
对于
YUV420sp
格式有以下两种,海思中使用的是
NV21
。也就是在这个上面被坑了许久。
NV12: YYYYYYYY UVUV =>YUV420SP
NV21: YYYYYYYY VUVU =>YUV420SP // 海思的视频格式应该是这种
之后,总算成功的获取了彩 {MOD}图片。虽然效率很低,但是也成功的保存了一张彩 {MOD}
BMP
图片。因为已经不做海思平台了,所以一下代码是我从程序中扣出来的,可能会有些错误。
int vi_getFrameInit()
{
HI_U32 u32Depth = 8;
VI_CHN ViChn = 0;
HI_S32 s32ret;
s32ret = HI_MPI_VI_SetFrameDepth(ViChn, u32Depth);
}
int vi_getYUV(char* yData, char* uData, char* vData)
{
HI_S32 s32ret;
VIDEO_FRAME_INFO_S stFrame;
HI_VOID* yPtr;
HI_VOID* uPtr;
HI_VOID* vPtr;
int yLen;
int yuvLen;
int w, h;
s32ret = HI_MPI_VI_GetFrame(ViChn, &stFrame, 2);
if(s32ret != 0){
return 0;
}
// 获取视频帧的Y分量大小
yLen = stFrame.stVFrame.u32Height*stFrame.stVFrame.u32Stride[0];
yuvLen = stFrame.stVFrame.u32Height * stFrame.stVFrame.u32Stride[0] * 3/2;
yPtr = (HI_CHAR*)HI_MPI_SYS_Mmap(stFrame.stVFrame.u32PhyAddr[0], yuvLen);
uPtr = yPtr + (stFrame.stVFrame.u32Stride[0]) * (stFrame.stVFrame.u32Height);
vPtr = uPtr + (stFrame.stVFrame.u32Stride[0]/2) * (stFrame.stVFrame.u32Height/2);
/* 1.get frame's Y */
memcpy(yData, yPtr, yLen);
/* 2.get frame's UV */
for (h = 0; h < stFrame.stVFrame.u32Height/2; h++)
{
for (w = 0; w < stFrame.stVFrame.u32Width/2; w++)
{
*(vData+h*stFrame.stVFrame.u32Width/2 + w) = *(uPtr);
*(uData+h*stFrame.stVFrame.u32Width/2 + w) = *(uPtr+1);
uPtr += 2;
}
}
HI_MPI_SYS_Munmap(yPtr, yuvLen);
(void)HI_MPI_VI_ReleaseFrame(ViChn, &stFrame);
return yuvLen;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
功能说明:把
NV21
格式的数据转化成为了
YUV
平面格式。其中
Y
分量存储在
yData
中,
U
分量存储在
uData
中,
V
分量存储在
vData
中。其实这个拷贝的方法效率极低。但是在不懂的时候,却成功的转换出了彩 {MOD}图片,所以还是挺有意义的。
上面的代码已经转化出了
YUV
平面格式的数据,但是我们要如何保存成
rgb
格式的数据呢?当然百度可以找到许多方法:
公式法什么的,但是抱歉初入图像处理,也不知道那种方法好用,公式是否正确,但是经过测试,下面这种比较笨的方法我成功了。这里也记录下来,不过作为一个对图像格式有点了解的人,现在完全可以自己直接用
公式法把
YUV420SP
格式的数据直接保存成
rgb
格式的数据。不过感觉目前来说,没有必要去测试那些公式,所以我把我原来老旧的方法分享一下。作为学习
openCV
来说也是不错的。其实我也是参考了这个贴吧中的方法:
yuv格式转为IplImage保存为图片怎么感觉 {MOD}彩有问题。
#define WIDTH 1920
#define HEIGHT 1080
int main(void)
{
char* y_data = new char[WIDTH*HEIGHT];
char* u_data = new char[WIDTH/2*HEIGHT/2];
char* v_data = new char[WIDTH/2*HEIGHT/2];
vi_getYUV(y_data, u_data, v_data);
IplImage *rgbimg = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 3);
IplImage *yimg = cvCreateImageHeader(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1);
IplImage *uimg = cvCreateImageHeader(cvSize(WIDTH/2, HEIGHT/2), IPL_DEPTH_8U, 1);
IplImage *vimg = cvCreateImageHeader(cvSize(WIDTH/2, HEIGHT/2), IPL_DEPTH_8U, 1);
IplImage *uuimg = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1);
IplImage *vvimg = cvCreateImage(cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1);
cvSetData(yimg, y_data, WIDTH);
cvSetData(uimg, u_data, WIDTH/2);
cvSetData(vimg, v_data, WIDTH/2);
cvResize(uimg, uuimg, CV_INTER_LINEAR);
cvMerge(yimg, uuimg, vvimg, NULL, rgbimg);
//可以重新设置rgb图像的大小
//IplImage *resizeRgbImg = cvCreateImage(cvSize(y_width/4, y_height/4), IPL_DEPTH_8U, 3);
//cvResize(rgbimg, resizeRgbImg, CV_INTER_LINEAR);
cvSaveImage("myImage.jpg", rgbimg);
cvReleaseImageHeader(&yimg);
cvReleaseImageHeader(&uimg);
cvReleaseImageHeader(&vimg);
cvReleaseImage(&rgbimg);
cvReleaseImage(&uuimg);
cvReleaseImage(&vvimg);
//cvReleaseImage(&resizeRgbImg);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
当然这个方法是比较笨的时候使用的方法,其实在上面已经获取了
YUV
平面格式的数据,可以考虑直接使用
cvCvtColor(yuvimage, rgbimg, CV_YCrCb2RGB);
得到
rgb
格式的数据。当然,直接使用
公式法应该是最简单的!
注意:上面的代码因为是手打的,所以可能会有错误!
总结
时间总是过得很快,所以成功也只是曾经的一瞬间,如果没有记录,以后用到也就不会有参考。其实,开发如果有些代码积累,然后照着代码去写,开发速度会很快。但是,如果没有记录,等于又要从头开始。所以,感觉要逐渐积累一些小例子,然后照着这些例子去写复杂代码就行。
学习!分享!感谢!
参考链接
OpenCV 中YUV420格式转换为IpImage格式