简体   繁体   中英

Bitwise math operations in C++

I'm trying to understand a function that reads and transforms depth data stored as 16-bit png file.

First they load the file into a opencv Mat of type CV_16UC1

cv::Mat depth_image = cv::imread(filename.c_str(), CV_LOAD_IMAGE_ANYDEPTH);

Then they assign

unsigned short * depth_raw = new unsigned short[frame_height * frame_width];
for (int i = 0; i < frame_height * frame_width; ++i) {
  depth_raw[i] = ((((unsigned short)depth_image.data[i * 2 + 1]) << 8) + ((unsigned short)depth_image.data[i * 2 + 0]));
  depth_raw[i] = (depth_raw[i] << 13 | depth_raw[i] >> 3);
  depth_data[i] = float((float)depth_raw[i] / 1000.0f);
}

Now I know that the "<<" operator in C++ is something like a bit shift, meaning 5 << 1 corresponds to the following bit-shift: "00000101" (which is 5 in binary) -> "00001010" (which is 10 in binary). So apparantly one can do multiplications and divisions by 2^n using "<< n" or ">> n".

Still I find it hard to understand the transformation above. Here is an example with numbers (applying cout to every step) for the above transformation:

depth_image.data[i] = 192
depth_image.data[2*i+1] = 47
depth_image.data[2*i+0] = 192
(((unsigned short)depth_image.data[i * 2 + 1]) << 8) = 12032
((unsigned short)depth_image.data[i * 2 + 0]) = 192
depth_raw[i] = 12224
depth_raw[i] << 13 = 0
depth_raw[i] >> 3 = 191
depth_raw[i] << 13 | depth_raw[i] >> 3 = 191
depth_data[i] = 1.528

What is really weird is the last line: It seems like the conversion from unsigned short to float is converting the number 191 into 1528 ???

Any help or hint will be appreciated.

Edit:
I found some Matlab code that shows how the authors saved the depth image previously:

% resave depth map with bit shifting
depthRaw = double(imread(filename))/1000;
saveDepth (depthRaw,newFilename);

function saveDepth (depth,filename)
    depth(isnan(depth)) =0;
    depth =single(depth)*1000;
    depthVis = uint16(depth);
    depthVis = bitor(bitshift(depthVis,3), bitshift(depthVis,3-16));
    imwrite(depthVis,filename);
end

So it looks like a weird saving...

Edit2:
Reply from the authors:
"The depth map is saved in a way that it shifts 3 bits to make the depth in PNG format more pleasing to human eyes. Therefore we need to shift it back during file reading".

there is no common norm, how data is stored. Therefore it may be neccessary to convert from little to big endian or the other way around. To understand endianess have a look here: https://en.wikipedia.org/wiki/Endianness

depth_raw[i] = ((((unsigned short)depth_image.data[i * 2 + 1]) << 8) + ((unsigned short)depth_image.data[i * 2 + 0]));

This statement is a convertion of endianess. the first byte is cast to unsigned short (from 8 to 16 bit) and then shifted right and then the second byte is added at the lower end. It basically swaps two bytes and converts it to an unsigned int.

depth_raw[i] = (depth_raw[i] << 13 | depth_raw[i] >> 3);
depth_data[i] = float((float)depth_raw[i] / 1000.0f);

After the convertion of endianess, the data has to be interpreted. The only way to be sure what the authors inteded to do here is to have a look at the documentation of the depth map. The first line moves the 3 least significant bits to the front and the others down. I have no idea, why this is done. I think the division by 1000 after that is only to correct for units (maybe m in mm or km in m), or it some kind of fixed point semantic. (represantion of rational number in interger data type).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM