看ORBSLAM2时算描述子之间的距离时看到的神奇的位操作,特此记录一哈。
unsigned int v = *pa ^ *pb;
v = v - ((v >> 1) & 0x55555555);
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
dist += (((v + (v >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24;
pa和pb分别是两个描述子,进行异或操作得到一个32位int值,里面包含多个1,1的个数和即为两个描述子之间的距离。
然后通过三个语句12个操作即可得到1的个数。
大概说一下我自己的解释,比较蠢吧,仅供参考。
0x55555555=01010101 01010101 01010101 01010101
我们把32位分成16个2位来看,即XX(X未知,可0可1),我们会发现int a=XX-(XX>>1)&01的结果即为XX中1的数量。
XX>>1=0X
然后会发现int a=XX-(XX>>1)&01后得到的int值即为XX中1的个数。即XX=00时,a=0,XX=01时,a=1,XX=10时,a=1,XX=11时,a=2。
那么同理,v=v-((v>>1)&0x55555555)得到了32位int值v,将此时的v每两位表示的int值相加即为原v中1的个数。
接下来
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
0x33333333=00110011 00110011 00110011 00110011
很明显,这一步操作是v拆成8个4位,每个4位代表的int值之和即为原v中1的个数,每个4位是由上式中的结果v的相邻2位拼接得到的。
同理可得v + (v >> 4)) & 0xF0F0F0F是把之前的相邻4位拼接成8位,则此时32位int值四个八位代表的int值之和即为1的个数。
之后乘0x1010101的目的是把每个8位的数加到25-32位上,然后右移24位移到末8位,即可得到四个八位的int值之和,即1的个数。