在《移植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 ch |