通常的摄像头读取的数据都是YUV422格式的,如果想要支持数码变焦,我们需要对他进行缩放操作,当然具体点应该是放大操作,因为我们都是把采集到的图像的局部放大,称为数码变焦.
在测试过程中尝试了很多不同的方法,包括了Gdiplus, Opencv, Ippi 以及自己编写的基于YUV422的插值操作.
这里简单介绍下基于Gdiplus, Opencv和Ippi的缩放操作. Gdiplus和Opencv都只能在YUV444上操作,为此,我们需要将YUV422转换为YUV444,有了YUV422,YUV444很容易得到,速度也很快. 通过Gdiplus, Opencv对YUV444进行缩放,将缩放后的YUV444再转换为YUV422即可完成整个缩放过程. 而Ippi提供了YUV422上的缩放函数,可以直接进行缩放操作.
具体的缩放如下:
Gdiplus:
int ZoomIn(BYTE* pOridata, int width, int height, CRect rect)//YUV444 { BITMAPINFOHEADER bmih; memset(&bmih, 0, sizeof(BITMAPINFOHEADER)); bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biWidth = width; bmih.biHeight = height; bmih.biPlanes = 1; bmih.biBitCount = 24; bmih.biSizeImage = width*height*bmih.biBitCount/8; Bitmap* pBitmap = Bitmap::FromBITMAPINFO((BITMAPINFO*)&bmih, pOridata); Bitmap* ptemp = pBitmap->Clone(rect.left, height-rect.bottom, rect.Width(), rect.Height(), pBitmap->GetPixelFormat()); Graphics gph(pBitmap); gph.DrawImage(ptemp, 0, 0, width, height); delete ptemp; delete pBitmap; return 0; }
Opencv:
YUV4222YUV444(pYUV422, pYUV444, iwidth, iheight); if (!pZoomsrcIplImg) { pZoomsrcIplImg = cvCreateImage(cvSize(iwidth, iheight), IPL_DEPTH_8U, 3); } if (pZoomsrcIplImg) { memcpy(pZoomsrcIplImg->imageData, pYUV444, g_iwidth*g_iheight*3); cvSetImageROI(pZoomsrcIplImg, cvRect(drawrect.left, drawrect.top, drawrect.Width(), drawrect.Height())); } if (!pZoomdstIplImg) { pZoomdstIplImg = cvCreateImage(cvSize(iwidth, iheight), IPL_DEPTH_8U, 3); } cvResize(pZoomsrcIplImg, pZoomdstIplImg, CV_INTER_LINEAR); memcpy(pYUV444, pZoomdstIplImg->imageData, iwidth*iheight*3); YUV4442YUV422(pYUV444, pYUV422, iwidth, iheight); cvResetImageROI(pZoomsrcIplImg);
Ippi:
IppiSize srcsize; srcsize.width = iwidth; srcsize.height = iheight; IppiRect srcROI; srcROI.x = (drawrect.left>>2)<<1; srcROI.y = drawrect.top; srcROI.width = (drawrect.Width()>>1)<<1; srcROI.height = (drawrect.Height()>>1)<<1; IppiSize dstsize; dstsize.width = iwidth; dstsize.height = iheight; double xFactor = double(dstsize.width)/srcROI.width; double yFactor = double(dstsize.height)/srcROI.height; ippiResizeYUV422_8u_C2R(pYUV422src, srcsize, g_iwidth*2, srcROI, pYUV422dst, iwidth*2, dstsize, xFactor, yFactor, IPPI_INTER_LINEAR); memcpy(g_pYUV422src, g_pYUV422dst, g_iwidth*g_iheight*2);
上述方法中drawrect为所需缩放区域, iwidth, iheight为缩放后的宽高(假设与原图像的整体宽高一致,即将图像部分区域缩放至全部)
利用双线性插值,本人写了一个基于YUV422的缩放代码,考虑到计算量的问题,效率无法和上述几种比较.(需要进行优化)
上述算法, Gdiplus与Opencv速度相似, Ippi速度要明显快于Gdiplus和Opencv. Ippi和Opencv均为intel所提供软件包.
本人所写基于YUV422的缩放:
int YUVZoomIn(void* pYUV, int width, int height, CRect rect) { if (rect.Width()%2!=0) { rect.left++; } if (rect.left%2!=0) { rect.left++; rect.right++; } BYTE* pZoomInData = new BYTE[width*height*2]; memset(pZoomInData, 0, width*height*2); double scale = ((double)(rect.right-rect.left))/width; BYTE* pOriYUV = (BYTE*)pYUV; BYTE* pZoonInYUV = (BYTE*)pZoomInData; double indexI, indexJ, u, v; int roundI, roundJ; int left, right, rectwidth, preU, preV; left=(rect.left>>1)<<1; right=(rect.right>>1)<<1; rectwidth=right-left; for (int i=0; i
测试可以使用,速度存在些问题,有改进想法的可以直接与我联系,哈哈
mosesyuan at gmail dot com