移植EMCV到DM6467(2)——OpenCV程序调试
来源:互联网 发布:数据库介绍ppt 编辑:程序博客网 时间:2024/06/05 20:02
在《移植EMCV到DM6467(1)》中已经实现了使用EMCV来创建图像并添加矩形框这么一个简单的功能,现在要在其基础上进行第二步操作。
首先,由于我们的DM6467开发板接收的视频来自TVP5150,PAL格式,颜色空间为YUV422semi-planar,而OpenCV中IplImage使用的是RGB格式图像,所以需要进行颜色空间转换。然后,由于EMCV中有很多C++风格的代码,需要改为C语言格式。
1 VS2010调试OpenCV程序
1.1 YUV转RGB
在嵌入式系统中,使用较多的是YUV颜色空间的图像,而在PC上则更多使用RGB格式。OpenCV中默认的也是使用RGB格式的图像,所以需要将YUV转换为RGB。
由于YUV是通过对RGB进行简单矩阵变换的来,所以转换比较方便,但是需要注意的是:TVP5150传输来的YUV视频数据其实真正的颜色空间格式应该是YCbCr,这与YUV之间有一点差异,所以在选择颜色空间转换的公式时需要特别注意,网上有很多转换的公式,但是很多人都没有说清楚到底是YUV转RGB还是YCbCr转RGB。如果转换公式使用错误,转换后的图像会变绿。
经过多方查找,比较和测试,最终选择下面这个公式进行转换,转换后的效果良好。
R = (1.164 * (Y - 16) + 1.596 * (V - 128))
G = (1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128))
B = (1.164 * (Y - 16) + 2.018 * (U - 128))
Y = ( 0.257 * R + 0.504 * G + 0.098 * B + 16 )
U = (-0.148 * R - 0.291 * G + 0.439 * B + 128)
V = ( 0.439 * R - 0.368 * G - 0.071 * B + 128)
DM6467是一款以C64+为核心的定点DSP,相比以C67+为核心的浮点DSP,对于同一个浮点运算,定点DSP所耗费的时间大约是浮点DSP的20倍。对于这么大的性能差异,我们在开发DM6467的应用程序时必须尽量少用浮点运算。所以,对于上面的YUV和RGB转换,我们需要考虑如何进行优化,将浮点运算改为整形运算。这个任务等整个移植封装过程都完成之后再做。
1.2 存储格式
DM6467的VPIF在接收了TVP5150传输过来的视频之后,会将其存储为一种特殊的格式,即YUV422 semi-planar,Y分量单独存储,UV分量交错存储。具体格式如下图所示。
而常见的YUV422存储格式如下图所示。
必须对两种存储格式有清晰的理解才能在编程时正确寻址并运算。
1.3 程序编写
在编程时,最重要的两点就是前面说的颜色空间转换公式以及YUV的存储格式,另外还需要注意的是OpenCV的IplImage存储的RGB图像数据是按BGR顺序存储的,还有就是在转换时可能会出现数据越界,需要添加限幅代码,即限制数据大小为0到255。只要注意到这些细节问题,编程不会有太大问题。最终的代码如下所示。
#include <stdio.h>
#include "cv.h"
#include "highgui.h"
#define MR(Y,U,V) (1.164 * (Y - 16) + 1.596 * (V - 128))
#define MG(Y,U,V) (1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128))
#define MB(Y,U,V) (1.164 * (Y - 16) + 2.018 * (U - 128))
#define MY(R, G, B) ( 0.257 * R + 0.504 * G + 0.098 * B + 16 )
#define MU(R, G, B) (-0.148 * R - 0.291 * G + 0.439 * B + 128)
#define MV(R, G, B) ( 0.439 * R - 0.368 * G - 0.071 * B + 128)
void YUV422_C_RGB( unsigned char* pYUV, unsigned char* pRGB, int height, int width);
void RGB_C_YUV422( unsigned char* pRGB, unsigned char* pYUV, int height, int width);
int main()
{
CvSize size; // pal frame, 422, YUV422 semi-planar
size.height = 576;
size.width = 720;
CvPoint point1, point2; // draw a rectangle in the middle of an image
point1.x = size.width / 2 - 50;
point1.y = size.height / 2 - 50;
point2.x = size.width / 2 + 50;
point2.y = size.height / 2 + 50;
CvScalar color = CV_RGB(0, 255, 0); // green
unsigned char * pYUV = NULL;
if((pYUV = (unsignedchar *)malloc(size.height * size.width * 2 * sizeof(unsigned char))) == NULL) {
printf("No enough memory to allocate buffer.\n");
}
FILE * yuvfile;
if((yuvfile = fopen("test.264","rb")) == NULL) {
printf("Can't open file test.264.\n");
}
fread(pYUV, sizeof(unsignedchar), size.height * size.width * 2, yuvfile);
fclose(yuvfile);
unsigned char * pRGB = NULL;
if((pRGB = (unsignedchar *)malloc(size.height * size.width * 3 * sizeof(unsigned char))) == NULL) {
printf("No enough memory to allocate buffer.\n");
}
YUV422_C_RGB(pYUV, pRGB, size.height, size.width);
IplImage* img;
img = cvCreateImage(size, IPL_DEPTH_8U, 3);
cvSetData(img, pRGB, size.width * 3);
cvRectangle(img, point1, point2, color, CV_AA, 0);
cvNamedWindow("rect", 1);
cvShowImage("rect", img);
cvWaitKey(0);
RGB_C_YUV422((unsigned char *)img->imageData, pYUV, size.height, size.width);
if((yuvfile = fopen("test1.264","wb")) == NULL) {
printf("Can't open file test1.264.\n");
}
fwrite(pYUV, sizeof(unsignedchar), size.height * size.width * 2, yuvfile);
fclose(yuvfile);
free(pYUV);
free(pRGB);
cvReleaseImageHeader(&img);
cvReleaseImage(&img);
return 0;
}
void YUV422_C_RGB( unsigned char* pYUV, unsigned char* pRGB, int height, int width)
{
unsigned char* pBGR = NULL;
unsigned char R = 0;
unsigned char G = 0;
unsigned char B = 0;
unsigned char Y = 0;
unsigned char U = 0;
unsigned char V = 0;
double tmp = 0;
for ( int i = 0; i < height; ++i )
{
for ( int j = 0; j < width; ++j )
{
pBGR = pRGB + i * width * 3 + j * 3;
Y = *(pYUV + i * width + j);
if(j % 2 ==0){
U = *(pYUV + height * width + i * width + j);
V = *(pYUV + height * width + i * width + j + 1);
}
else {
U = *(pYUV + height * width + i * width + j - 1);
V = *(pYUV + height * width + i * width + j);
}
tmp = MB(Y, U, V);
tmp = (tmp > 255) ? 255 : tmp;
tmp = (tmp < 0) ? 0 : tmp;
B = (unsigned char)tmp;
tmp = MG(Y, U, V);
tmp = (tmp > 255) ? 255 : tmp;
tmp = (tmp < 0) ? 0 : tmp;
G = (unsigned char)tmp;
tmp = MR(Y, U, V);
tmp = (tmp > 255) ? 255 : tmp;
tmp = (tmp < 0) ? 0 : tmp;
R = (unsigned char)tmp;
*pBGR = B;
*(pBGR + 1) = G;
*(pBGR + 2) = R;
}
}
}
void RGB_C_YUV422( unsigned char* pRGB, unsigned char* pYUV, int height, int width)
{
unsigned char* pBGR = NULL;
unsigned char R = 0;
unsigned char G = 0;
unsigned char B = 0;
unsigned char Y = 0;
unsigned char U = 0;
unsigned char V = 0;
double tmp = 0;
for ( int i = 0; i < height; ++i )
{
for ( int j = 0; j < width; ++j )
{
pBGR = pRGB + i * width * 3 + j * 3;
B = *pBGR;
G = *(pBGR + 1);
R = *(pBGR + 2);
tmp = MY(R, G, B);
tmp = (tmp > 255) ? 255 : tmp;
tmp = (tmp < 0) ? 0 : tmp;
Y = (unsigned char)tmp;
*(pYUV + i * width + j) = Y;
if(j % 2 == 0) {
tmp = MU(R, G, B);
tmp = (tmp > 255) ? 255 : tmp;
tmp = (tmp < 0) ? 0 : tmp;
U = (unsigned char)tmp;
*(pYUV + height * width + i * width + j) = U;
}
else {
tmp = MV(R, G, B);
tmp = (tmp > 255) ? 255 : tmp;
tmp = (tmp < 0) ? 0 : tmp;
V = (unsigned char)tmp;
*(pYUV + height * width + i * width + j) = V;
}
}
}
}
程序使用的test.264是由TVP5150获取的原始视频帧,如下图所示。
经过处理(即添加矩形框)之后,的图像如下图所示。
2 DSP端调试
2.1 EMCV修改
在完成了VS2010中调试OpenCV程序之后,需要将其移植到DSP。考虑到嵌入式系统中一般都是用C语言而不是C++,所以需要将EMCV的源码该为C语言格式。主要的修改包括以下几个地方。
1,变量定义位置问题
在C语言中,所有的变量定义都需要放在函数的最前面,而在C++中,可以在函数中随时定义新的变量。对于下面的例子,在for循环中定义了新的变量i和j,这在C语言中是不允许的。
unsigned char* pBGR = NULL;
unsigned char R = 0;
unsigned char G = 0;
unsigned char B = 0;
unsigned char Y = 0;
unsigned char U = 0;
unsigned char V = 0;
double tmp = 0;
for ( int i = 0; i < height; ++i )
{
for ( int j = 0; j < width; ++j )
{
……
经过修改,上面的部分代码应该改为:
unsigned char* pBGR = NULL;
unsigned char R = 0;
unsigned char G = 0;
unsigned char B = 0;
unsigned char Y = 0;
unsigned char U = 0;
unsigned char V = 0;
double tmp = 0;
int i, j;
for (i = 0; i < height; ++i)
{
for (j = 0; j < width; ++j)
{
……
2,函数调用的参数问题
在C++中,调用函数时可以不用传递函数定义了的那么多个实参,而C语言要求比较严格,函数定义了多少个形参,调用时就必须有多少个实参。对于下面的例子,C++中调用cvRectangle函数时只传递了6个参数,虽然函数定义中有7个参数也不会出错,但是,在C语言中就不行。
cvRectangle(img, point1, point2, color, CV_AA, 0);
函数原型为:
CV_IMPL void
cvRectangle( void* img, CvPoint pt1, CvPoint pt2,
CvScalar color, int thickness,
int line_type, int shift )
经过修改,上面的函数调用改为:
cvRectangle(img, point1, point2, color, CV_AA, 8, 0);
3,数组赋值问题
在C++中,支持在数组初始化时对其进行变量赋值,如下所示。
void func(x, y, z)
{
int a[] = {x, y, z};
……
而在C语言中,数组初始化时赋值必须是常量赋值。所以上面的代码需要修改为:
void func(x, y, z)
{
int a[3];
a[0] = x;
a[1] = y;
a[2] = z;
……
另外,还有一些细节问题,例如包含头文件的相对路径等等,这里就不细说了。
2.2 程序编写
在修改完EMCV所有代码为C语言代码之后,需要编写主程序实现在图像中画矩形框。鉴于先前已经在VS2010中编写好了相关代码,所以现在只需移植过来做简单修改即可。重点要注意的是内存分配问题,在DSP中分配内存空间时需要使用calloc而不是malloc来分配连续的内存空间,否则在进行颜色空间转换时,可能会把程序的其他段给覆盖掉导致程序损坏而无法继续运行。修改过后的内存空间分配代码如下:
if((pYUV = (unsigned char *)calloc(size.height * size.width * 2, sizeof(unsigned char))) == NULL)
printf("No enough memory to allocate buffer.\n");
我们使用的图片分辨率为720 X 576,颜色空间为YUV422,每一帧图像大小为810KB,转换为RGB后为1215KB,这需要占用比较大的内存空间,需要在cmd文件中对堆区分配较大空间,我们这里设置为0x04000000。
另外,由于在DSP中没有文件系统概念,不能读取文件,所以对于缓冲区我们进行手动填充(也可以使用CCS中的load data功能),填充代码如下。
// fill the image with balck
for(i = 0; i < size.height * size.width; i++) {
*(pYUV + i) = 0x10;
*(pYUV + size.height * size.width + i) = 0x80;
}
经过以上修改,编译程序并下载到开发板上运行,发现程序运行正常。
- 移植EMCV到DM6467(2)——OpenCV程序调试
- 移植EMCV到DM6467(3)——CCS调试XDAIS算法
- 移植EMCV到DM6467(3)——CCS调试XDAIS算法
- 移植EMCV到DM6467(1)——C++工程测试
- EMCV移植到DM6467(一)
- 移植EMCV到DM6467(4)——video_copy例程的xDM算法封装
- 移植EMCV到DM6467(5)——修改encodedecode demo测试算法封装
- 移植EMCV到DM6467(4)——video_copy例程的xDM算法封装
- 移植EMCV到DM6467(5)——修改encodedecode demo测试算法封装
- emcv/opencv 移植到DM647/DM648
- OpenCV 移植学习--EMCV
- emcv移植到DM642
- 移植OpenCV的AdaBoost人脸检测算法到DM6467
- 移植OpenCV的AdaBoost人脸检测算法到DM6467
- 移植OpenCV的AdaBoost人脸检测算法到DM6467
- 移植OpenCV的AdaBoost人脸检测算法到DM6467
- 移植OpenCV到DM6467的ARM端并集成到C6Accel
- DM6467开发之U-Boot移植(2)——U-Boot移植
- JDK中的URLConnection参数详解
- Notepad++ 是程序员的必备利器之一
- 07-MyBatis_sql标签和include标签的使用
- 最大子矩阵求和问题
- 基于distanceTransform-距离变换的手掌中心提取
- 移植EMCV到DM6467(2)——OpenCV程序调试
- Leetcode: N-Queens
- eclipse 如何让c++不报错
- SDP协议及其应用
- ubunto用户切换
- 智能搜索算法--从A*算法开始说起
- 笔试回顾
- 华人及一季度和共和党人捷易通
- ACM题目:魔术数 M=K*Mc ,Mc为M的变形 比如M=102564,Mc=25641,102564=4*25641。