openCV HSV转RGB函数解析(内层循环和外层循环对算法效率的影响)

我们先看一下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_HSV
tab[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 的话,此算法的效率会有所下降,但是也相差无几!

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

你可能感兴趣的:(算法分析,算法,opencv,RGB,HSV)