移植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;

         }

 

经过以上修改,编译程序并下载到开发板上运行,发现程序运行正常。