Reputation: 6496
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".
Upvotes: 2
Views: 2010
Reputation: 2004
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).
Upvotes: 3