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
//特化版本了
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成正数了啊...
这篇太恶心了!
到这里就结束了!