opencv2.0 saturate_cast<?> 研究

opencv对于强制类型转换写了自己的模板类,我们来研究一下...

突然发现自己的c++语法该补一补了...

函数模板的特化  有点难懂...但起码我知道了模板还有特化之说

/////////////// saturate_cast (used in image & signal processing) ///////////////////

//这里就应该理解为泛化版本的了
template<typename _Tp> static inline _Tp saturate_cast(uchar v) { return _Tp(v); }
template<typename _Tp> static inline _Tp saturate_cast(schar v) { return _Tp(v); }
template<typename _Tp> static inline _Tp saturate_cast(ushort v) { return _Tp(v); }
template<typename _Tp> static inline _Tp saturate_cast(short v) { return _Tp(v); }
template<typename _Tp> static inline _Tp saturate_cast(unsigned v) { return _Tp(v); }
template<typename _Tp> static inline _Tp saturate_cast(int v) { return _Tp(v); }
template<typename _Tp> static inline _Tp saturate_cast(float v) { return _Tp(v); }
template<typename _Tp> static inline _Tp saturate_cast(double v) { return _Tp(v); }

一个个看呗,要有耐心,研究之后,以后写代码就不会概念模糊了...

转uchar

扫描二维码关注公众号,回复: 2738715 查看本文章
//特化版本了
template<> inline uchar saturate_cast<uchar>(schar v) //schar
{ return (uchar)std::max((int)v, 0); }
template<> inline uchar saturate_cast<uchar>(ushort v) //ushort
{ return (uchar)std::min((unsigned)v, (unsigned)UCHAR_MAX); }
template<> inline uchar saturate_cast<uchar>(int v) //int
{ return (uchar)((unsigned)v <= UCHAR_MAX ? v : v > 0 ? UCHAR_MAX : 0); }
template<> inline uchar saturate_cast<uchar>(short v) //short
{ return saturate_cast<uchar>((int)v); }
template<> inline uchar saturate_cast<uchar>(unsigned v) //uint
{ return (uchar)std::min(v, (unsigned)UCHAR_MAX); }
template<> inline uchar saturate_cast<uchar>(float v) //float
{ int iv = cvRound(v); return saturate_cast<uchar>(iv); }
template<> inline uchar saturate_cast<uchar>(double v) //double
{ int iv = cvRound(v); return saturate_cast<uchar>(iv); }

注意:这里讨论字符的ASCII码值...

uchar转uchar:这个如果这么做的话,因为没有这个特化版本,而有泛化版本,经过测试,会选择泛化版本,而不是选择特化版本的强制转换实参类型。
schar转uchar:schar的范围是【-128,127】,而uchar的范围是【0,255】,很明显schar的【-128,-1】全部被映射为0了。

ushort转uchar: ushort的范围是【0,65535】,很明显将【256,65535】映射为255了。

int转uchar:int的范围是【-2147483648,2147483647】,这个玩得有点遛啊,两个三目运算符嵌套,解析开来就是:

if(v > 0) ans = (unsigned)v <= 255 ? v : 255;
else ans = (unsigned)v <= 256 ? v : 0; 

也就是当v<0时,全部映射为0;v=0时,映射为0; 0 < v <=255 时,不变;v > 255 时,映射为255.

这写库的人也是牛逼啊,这么高的技巧都玩的出来,我们只能写写if/else了

short转uchar:short的范围【-32768,32767】,调用了int转uchar,所以结论和int转uchar的一样

uint转uchar:uint的范围【0,4294967295】,很明显【256,42949672950】映射为了255

float/double转uchar:这个更牛逼了,直接和Intel的硬件挂钩了,可以观摩一下代码

CV_INLINE  int  cvRound( double value )
{
#if (defined _MSC_VER && defined _M_X64) || (defined __GNUC__ && defined __x86_64__ && !defined __APPLE__)
    __m128d t = _mm_set_sd( value );
    return _mm_cvtsd_si32(t);
#elif defined _MSC_VER && defined _M_IX86
    int t;
    __asm
    {
        fld value;
        fistp t;
    }
    return t;
#elif defined HAVE_LRINT || defined CV_ICC || defined __GNUC__
    return (int)lrint(value);
#else
    // while this is not IEEE754-compliant rounding, it's usually a good enough approximation
    return (int)(value + (value >= 0 ? 0.5 : -0.5));
#endif
}

可以参考一下这篇博客 http://blog.sina.com.cn/s/blog_675662490100idlj.html ,这个有兴趣的童鞋自己研究一下...

本博主也不怎么会,暂且用执行#else部分来解释了,就是四舍五入,负数时小数部分按绝对值四舍五入,但这里有一个问题,double的有效位数可以到达15~16位,这样就存在爆int的情况,经测试double的整数部分若在【-2147483648,2147483647】这个范围内,那么直接截取整数部分,否则就取-2147483648,那么也就是超出int范围的数会被映射为0,其他情况同int的映射方式

为了方便,opencv其实是定义了一堆宏的,贴出来供大家查询

#define SCHAR_MIN	(-128)
#define SCHAR_MAX	127

#define UCHAR_MAX	255

/* TODO: Is this safe? I think it might just be testing the preprocessor,
 *       not the compiler itself... */
#if	('\x80' < 0)
#define CHAR_MIN	SCHAR_MIN
#define CHAR_MAX	SCHAR_MAX
#else
#define CHAR_MIN	0
#define CHAR_MAX	UCHAR_MAX
#endif

/*
 * Maximum and minimum values for ints.
 */
#define INT_MAX		2147483647
#define INT_MIN		(-INT_MAX-1)

#define UINT_MAX	0xffffffff

/*
 * Maximum and minimum values for shorts.
 */
#define SHRT_MAX	32767
#define SHRT_MIN	(-SHRT_MAX-1)

#define USHRT_MAX	0xffff

/*
 * Maximum and minimum values for longs and unsigned longs.
 *
 * TODO: This is not correct for Alphas, which have 64 bit longs.
 */
#define LONG_MAX	2147483647L
#define LONG_MIN	(-LONG_MAX-1)

#define ULONG_MAX	0xffffffffUL


特别是定义CHAR_MIN和CHAR_MAX,写库者都有疑问,因为这可能与编译环境有关...我等渣渣就更不知道了...

转schar

template<> inline schar saturate_cast<schar>(uchar v) //uchar
{ return (schar)std::min((int)v, SCHAR_MAX); }
template<> inline schar saturate_cast<schar>(ushort v) //ushort
{ return (schar)std::min((unsigned)v, (unsigned)SCHAR_MAX); }
template<> inline schar saturate_cast<schar>(int v) //int
{
    return (schar)((unsigned)(v-SCHAR_MIN) <= (unsigned)UCHAR_MAX ?
                v : v > 0 ? SCHAR_MAX : SCHAR_MIN);
}
template<> inline schar saturate_cast<schar>(short v) //short
{ return saturate_cast<schar>((int)v); }
template<> inline schar saturate_cast<schar>(unsigned v) //uint
{ return (schar)std::min(v, (unsigned)SCHAR_MAX); }

template<> inline schar saturate_cast<schar>(float v) //float
{ int iv = cvRound(v); return saturate_cast<schar>(iv); }
template<> inline schar saturate_cast<schar>(double v) //double
{ int iv = cvRound(v); return saturate_cast<schar>(iv); }

schar转schar:选择泛化版本

uchar转schar:【128,255】映射为127

ushort转schar:【128,65535】映射为127

int转schar:【-2147483648,-129】映射为-128,【128,2147483647】映射为127,我看着也觉得好难判断,不知道写库的人是怎么想到能用2个三目运算符嵌套的...好可怕!

short转schar:【-32768,-129】映射为-128,【128,32767】映射为127

uint转schar:【128,4294967295】映射为127

float/double转schar:如果爆int的范围,那么映射为-128,否则与int的映射方法一致

发现一个神坑的地方当调用saturate_cast<char>的时候,是直接强制转成char的,所以注意schar与char编译器认为不同...C++标准说有三种char,signed char, unsigned char...好无语...


转ushort

template<> inline ushort saturate_cast<ushort>(schar v) //schar 
{ return (ushort)std::max((int)v, 0); }
template<> inline ushort saturate_cast<ushort>(short v) //short
{ return (ushort)std::max((int)v, 0); }
template<> inline ushort saturate_cast<ushort>(int v) //int
{ return (ushort)((unsigned)v <= (unsigned)USHRT_MAX ? v : v > 0 ? USHRT_MAX : 0); }
template<> inline ushort saturate_cast<ushort>(unsigned v) //uint
{ return (ushort)std::min(v, (unsigned)USHRT_MAX); }
template<> inline ushort saturate_cast<ushort>(float v) //float
{ int iv = cvRound(v); return saturate_cast<ushort>(iv); }
template<> inline ushort saturate_cast<ushort>(double v) //double
{ int iv = cvRound(v); return saturate_cast<ushort>(iv); }

ushort转ushort:选择泛化版本

uchar转ushort:选择泛化版本,强制将uchar转成ushort,映射范围不变

schar转ushort:【-128,-1】映射为0

short转ushort:【-32768,-1】映射为0

int转ushort:【-2147483648,-1】映射为0,【65536,2147483647】映射为65535

uint转ushort:【65536,4294967295】映射为65535

float/double转ushort:超过int范围,映射为0,否则与int的映射方法一致


转short

template<> inline short saturate_cast<short>(ushort v) //ushort
{ return (short)std::min((int)v, SHRT_MAX); }
template<> inline short saturate_cast<short>(int v) //int
{
    return (short)((unsigned)(v - SHRT_MIN) <= (unsigned)USHRT_MAX ?
            v : v > 0 ? SHRT_MAX : SHRT_MIN);
}
template<> inline short saturate_cast<short>(unsigned v) //uint
{ return (short)std::min(v, (unsigned)SHRT_MAX); }
template<> inline short saturate_cast<short>(float v) //float
{ int iv = cvRound(v); return saturate_cast<short>(iv); }
template<> inline short saturate_cast<short>(double v) //double
{ int iv = cvRound(v); return saturate_cast<short>(iv); }

uchar/char/short转short:选择泛化版本,映射范围不变
ushort转short:【32768,65535】映射为32767

int转short:【-2147483648,-32769】映射为-32768,【32768,2147483647】映射为32767

float/double转short:超出int范围,映射为-32768,否则与int的映射方法一致


转int

template<> inline int saturate_cast<int>(float v) { return cvRound(v); }
template<> inline int saturate_cast<int>(double v) { return cvRound(v); }

uchar/char/ushort/short/int转int:选择泛化版本,范围不变

uint转int:选择泛化版本,这里将【2147483648,4294967285】映射为【-2147483648,-1】这里非常特殊

float/double转int:超出int范围,映射为-2147483648,否则与int的映射方法一样


转uint

// we intentionally do not clip negative numbers, to make -1 become 0xffffffff etc.
template<> inline unsigned saturate_cast<unsigned>(float v){ return cvRound(v); }
template<> inline unsigned saturate_cast<unsigned>(double v) { return cvRound(v); }

这个更蛋疼了...

除了float和double之外,其余都调用泛化版本。

uchar/ushort/uint转uint:范围不变

char转uint:【0,127】不变,【-128,-1】映射为【4294967168,4294967295】特殊

short转uint:【0,32767】不变,【-32768,-1】映射为【4294934528,4294967295】特殊

int转uint:【0,2147483647】不变,【-2147483648,-1】映射为【2147483648,4294967295】特殊

float/double转uint:超出int范围,映射为2147483648,否则与int的映射方法一样


转float/double

这个都直接调用强制类型转换

double还好,基本上没有误差...

 但float只有7位有效精度,所以当int和uint绝对值非常大时,存在精度严重损失...

注:opencv里没有定义宏uint,写代码时替换成unsigned即可。


由于转换关系实在太复杂,上面可能会有一些错误,请广大网友写代码测试,如果发现错误,还望及时纠正!

总结:

1.对于转成schar、char、ushort、short这四种类型还是有规律的,基本上遵循两条的原则:

原则一:当超出上界时,调整为上界

原则二:当超出下界时,调整为下界

2.对于转成int,除了uint奇葩点(上面已标注),其余都不变

3.对于转成uint,对于全部的无符号类型不变,有符号类型,正数部分不变,负数时同步加上4294967296

4.对于转成float、double型,注意float的精度


对于第2,3条为什么会这么奇葩,是因为写库者没有flip...不知道他说的flip是什么意思,我看负数全部filp成正数了啊...

这篇太恶心了!

到这里就结束了!






猜你喜欢

转载自blog.csdn.net/u012866104/article/details/50811783
今日推荐