我们先看一下openCV的源码:
开始调用的是这个函数:
static CvStatus CV_STDCALL
 icvHSV2BGRx_8u_C3CnR( const uchar* src, int srcstep, uchar* dst, int dststep,
                       CvSize size, int dst_cn, int blue_idx )
接下来:
static CvStatus CV_STDCALL
 icvABC2BGRx_8u_C3CnR( const uchar* src, int srcstep, uchar* dst, int dststep,
                       CvSize size, int dst_cn, int blue_idx, CvColorCvtFunc2 cvtfunc_32f,
                      const float* pre_coeffs, int postscale )再接下来就是核心运算函数:
static CvStatus CV_STDCALL
 icvHSV2BGRx_32f_C3CnR( const float* src, int srcstep, float* dst,
                        int dststep, CvSize size, int dst_cn, int blue_idx )
我们分析一下,最后调用的核心函数的内容:

static CvStatus CV_STDCALL
 icvHSV2BGRx_32f_C3CnR( const float* src, int srcstep, float* dst,
                        int dststep, CvSize size, int dst_cn, int blue_idx )
 {
     int i;
     srcstep /= sizeof(src[0]);
     dststep /= sizeof(dst[0]);
     dststep -= size.width*dst_cn;
     size.width *= 3;


     for( ; size.height--; src += srcstep, dst += dststep )
     {
         for( i = 0; i < size.width; i += 3, dst += dst_cn )
         {
             float h = src[i], s = src[i+1], v = src[i+2];
             float b, g, r;

//判断s 为零,本人觉得这个地方有点问题,因为浮点类型不可以这么对比//可以修改为
//#define YX_FLOAT_EQUAL_ZERO(f) ((f) < FLT_EPSILON && (f) > -FLT_EPSILON)
//当然了,你先要添加这个宏定义才行啊
             if( s == 0 )
                 b = g = r = v;
             else
             {
                 static const int sector_data[][3]=
                     {{1,3,0}, {1,0,2}, {3,0,1}, {0,2,1}, {0,1,3}, {2,1,0}};
                 float tab[4];
                 int sector;//相当于 h /= 60;    因为:1.0 / 60 = 0.016666666666666666f;
                h *= 0.016666666666666666f; 
//这是把h界定在0~5之间
                 if( h < 0 )
                     do h += 6; while( h < 0 );
                 else if( h >= 6 )
                     do h -= 6; while( h >= 6 );
                 sector = cvFloor(h);//取h的小数部分
                 h -= sector;

//下面的操作在定义中有了,不详细描述了(看下面的维基链接)//https://en.wikipedia.org/wiki/HSL_and_HSVtab[0] = v;
tab[1] = v*(1.f - s);
tab[2] = v*(1.f - s*h);
tab[3] = v*(1.f - s*(1.f - h));
                 
                 b = tab[sector_data[sector][0]];
                 g = tab[sector_data[sector][1]];
                 r = tab[sector_data[sector][2]];
             }


dst[blue_idx] = b;
dst[1] = g;
dst[blue_idx^2] = r;
if( dst_cn == 4 )
dst[3] = 0;
         }
     }


     return CV_OK;
 }
其实,程序很简单,到这里也就结束了,没什么奇怪的地方。
可是我把程序重新写了一遍,发现了很大的不同。
直接读代码吧:
这段代码是上面列举的第二个函数,分析在最后面
static CvStatus CV_STDCALL
 icvABC2BGRx_8u_C3CnR( const uchar* src, int srcstep, uchar* dst, int dststep,
 CvSize size, int dst_cn, int blue_idx, CvColorCvtFunc2 cvtfunc_32f,
 const float* pre_coeffs, int postscale )
 {
int block_size = MIN(1 << 8, size.width);
float* buffer = (float*)cvStackAlloc( block_size*3*sizeof(buffer[0]) );
int i, di, k;
CvStatus status = CV_OK;


     dststep -= size.width*dst_cn;


     for( ; size.height--; src += srcstep, dst += dststep )
     {
         for( i = 0; i < size.width; i += block_size )
         {
             const uchar* src1 = src + i*3;
             di = MIN(block_size, size.width - i);
             
for( k = 0; k < di*3; k += 3 )
{
float a = CV_8TO32F(src1[k])*pre_coeffs[0] + pre_coeffs[1];
float b = CV_8TO32F(src1[k+1])*pre_coeffs[2] + pre_coeffs[3];
float c = CV_8TO32F(src1[k+2])*pre_coeffs[4] + pre_coeffs[5];
buffer[k] = a;
buffer[k+1] = b;
buffer[k+2] = c;
}


             status = cvtfunc_32f( buffer, 0, buffer, 0, cvSize(di,1), 3, blue_idx );
             if( status < 0 )
                 return status;
             
             if( postscale )
             {
                 for( k = 0; k < di*3; k += 3, dst += dst_cn )
                 {
                     int b = cvRound(buffer[k]*255.);
                     int g = cvRound(buffer[k+1]*255.);
                     int r = cvRound(buffer[k+2]*255.);


                     dst[0] = CV_CAST_8U(b);
                     dst[1] = CV_CAST_8U(g);
                     dst[2] = CV_CAST_8U(r);
                     if( dst_cn == 4 )
                         dst[3] = 0;
                 }
             }
             else
             {
for( k = 0; k < di*3; k += 3, dst += dst_cn )
{
int b = cvRound(buffer[k]);
int g = cvRound(buffer[k+1]);
int r = cvRound(buffer[k+2]);


dst[0] = CV_CAST_8U(b);
dst[1] = CV_CAST_8U(g);
dst[2] = CV_CAST_8U(r);
if( dst_cn == 4 )
dst[3] = 0;
}
             }
         }
     }


     return CV_OK;
 }其实这段代码也没有什么,只是看上去有点奇怪,怪在哪里呢?
就是函数中本来只存在一个大循环(size.height==1),就是for( i = 0; i < size.width; i += block_size );
内部一个循环:for( k = 0; k < di*3; k += 3 );
问题出来了,为什么程序编写者不在内存循环中,把结果直接计算出来?实际上,作者是把内层的循环分成了3部分来完成。
第一部分:
for( k = 0; k < di*3; k += 3 )
{
float a = CV_8TO32F(src1[k])*pre_coeffs[0] + pre_coeffs[1];
float b = CV_8TO32F(src1[k+1])*pre_coeffs[2] + pre_coeffs[3];
float c = CV_8TO32F(src1[k+2])*pre_coeffs[4] + pre_coeffs[5];
buffer[k] = a;
buffer[k+1] = b;
buffer[k+2] = c;
}第二部分:
 status = cvtfunc_32f( buffer, 0, buffer, 0, cvSize(di,1), 3, blue_idx ); 即核心运算函数 icvHSV2BGRx_32f_C3CnR
第三部分:
for( k = 0; k < di*3; k += 3, dst += dst_cn )
{
int b = cvRound(buffer[k]);
int g = cvRound(buffer[k+1]);
int r = cvRound(buffer[k+2]);


dst[0] = CV_CAST_8U(b);
dst[1] = CV_CAST_8U(g);
dst[2] = CV_CAST_8U(r);
if( dst_cn == 4 )
dst[3] = 0;
}


这三部分完全一次计算出来呀,为什么要分开呢?

按照这个思路,本人把三个内循环程序拆开,直接计算出结果,最后得到的结论是:

之前openCV程序的效率是:0.2738秒(图像1920*1080 debug版),而我的计算是:0.3897秒(图像1920*1080 debug版),耗时约1.4倍于openCV的源码

从这里可以看出来,分块处理的有效性,大家有兴趣可以测试一下!

如果再加入一层循环,也就是说最外层的大循环size.height!=1 的话,此算法的效率会有所下降,但是也相差无几!

可以看出,算法优化的最根本的部分,在于内层循环的优化,外层循环影响并不是很大!