-
Notifications
You must be signed in to change notification settings - Fork 769
Added filtering of shadowed color regions to registration #253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Awesome, I was planning for something similar, but I wouldn't have assumed that it's actually that fast. I'll merge this soon-ish. |
rx = wx / (color.fx * color_q); | ||
ry = wy / (color.fx * color_q); | ||
rx = (wx / (color.fx * color_q)) - (color.shift_m / color.shift_d); | ||
ry = (wy / (color.fx * color_q)) * color.fy + color.cy; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be color.fy
in the second line? Then it could also be simplified to ry = (wy / color_q) + color.cy;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it should be fy
, I didn't checked the previous code for errors. But from the senser provided intrinsics fy
and fx
are always the same. But if someone uses other intrinsics this could lead to wrong values, I will change it.
I was impressed too. After restructuring the maps and using pointers it went down to 2.3 ms. With the static computations added to the maps it went down to 1.9 ms. With the extra map for |
Does this include depth filtering? I think that's the hard part, involving a depth map of 1920x1080 (or half the size). |
@xlz you mean depth buffer? Not yet. I have the undistortion working now, unfortunately in a separate branch. Currently checking what's the best way to integrate this PR with my code. |
Well, this is now only a color -> depth registration, so there won't be a high resolution depth map. I though about a depth -> color registration using the inverse of this registration, but didn't found a way to do this, while |
This one does depth filter: https://github.com/xlz/libfreenect2/commit/a0c0b6e5402ad363dcd5bbe58ebab8d093ea2f80 What I meant for depth map is an intermediate map to filter duplicate pixels, just as you described. The fancy something is erosion which extracts minimum of neighboring pixels. Small erosion kernel size will produce aliasing in the edge. There was unavoidable aliasing with erosion on a half-size depth filter so I chose full-size. In depth to color registration, it is upsampling the depth image so higher depth resolution wouldn't mean higher depth quality. In color to depth registration, even naive downsampling of RGB image will still retain very RGB quality in the pointcloud. |
Nice. I though of something like this, setting a whole neighborhood instead of one pixel. But this filtering is pretty expensive, especially the erode. Yeah, you can't add information when upsampling, but you will get more detailed texture in the point cloud. It always depends on what you want. |
I updated the pull request. It now includes undistortion of the depth and registered color image. It is still arround 0.9 to 1.0 ms. on my system. There was also a double conversion in one of the if statements, which I also replaced. |
Great! Just one small additional request: could you add some comments to your code? There's a fair amount of pointer arithmetic going on, and it's rather easy to lose track... |
Added some comments, hope it is easier to keep track now. |
I just implemented the filtering from @xlz into the registration, but without any OpenCV. I tried to optimize the filtering as well. The hole registration with filtering takes now ~5.5 ms. on my system. @xlz: could you check if the filtering is fine like this and if it works similar to your implementation? |
Good work. Unfortunately I won't be able to test it for a while due to traveling etc. I roughly looked at the new code. Most looks good. One issue: don't assume 3 bytes per pixel. Hardware decoders (Tegra, Vaapi) will return RGBA format. IIUC this is still a 5x3 filter on a 1920x1080 image. If you haven't done so, also verify if there is any aliasing with maximum depth range. |
@xlz ok, didn't know that there are rgba color images. Is the byte order @HenningJ the filter only works if there are depth values. At the borders of objects, like the cup, you don't get depth values, so there are no foreground depth pixel which use the color pixel from the border of the cup. Only the depth pixel from the background map to those border pixels, therefore they get used for those background depth pixels. |
RGBA yes. If you see nothing strange then no aliasing. Do not need fancy filtering here. Optional sure. |
What I did in my (unmerged) registration code is to simply treat all color image data as RGBA (i.e. registered color images are output as RGBA, and input images are assumed to be RGBA, too). Only difference for RGB images is that the source pointer is advanced by 3 instead of 4 bytes. IMHO, this has two advantages:
However, I would not implement this within this current PR. @wiedemeyer if possible, I'd like to split this into several PRs:
|
I tried to cherry-pick the commits except the last one (97976ea), but apparently Github isn't smart enough to fast-forward the PR on its own. Can you try and see what happens if you merge master into this PR? |
I rebased it to the current master. But I saw you already pulled the first commits. |
@wiedemeyer thanks for the explanation. |
Just to make sure, the color image from the CPU JPEG endcoding has the |
Yes, of course. I saw it right after I pushed. |
enough pestering, gotta get back to work ;-) |
If the input image is 3 byte the alpha channel of the output image will be set to the blue channel value of the next pixel. |
Yeah, that's what I meant.
This will happen now anyways, won't it? You're still writing a four byte (unsigned) int when there are only 3 bytes left in the 3 byte output image. You're just writing 0 to the additional byte, instead of some other number. If there is anything at that byte, it's gonna be bad either way. So I don't see any scenario where the additional &-operation actually helps. |
Ok, the |
Well...if anybody needs that byte to be 0, then of course your implementation is correct, it's just the comment thats wrong. ;-) |
I think that we should simply change |
I tried it out, and it makes things much more beautiful. |
should I push the changes to this pull request so you can try them out? |
Yes, please. Regarding BGRA vs RGBA: we're trying to get rid of OpenCV anyway, the viewer from PR #261 is based on OpenGL and can use RGBA. But for the moment, TJPF_BGRX would probably be the best choice. |
Updated registration and removed handling of 3 byte color images. Updated protonect to display color image correct.
I updated the PR. |
OK, from what I've seen, any OpenGL implementation which can deal with libfreenect2 should also be able to handle BGRA just fine. |
VA-API can output both. Tegra can only output RGBA. |
For the registration, the color order probably doesn't even matter now that we simply treat all individual pixels as |
Yeah, the order does not matter for the registration. The registration with filter takes ~5.7 ms. and 1.0 ms. without. So no real changes. |
Just let both versions run of the jpeg decoder run and both are around 14 ms. |
Excellent, good to know. Merged. |
Added filtering of shadowed color regions to registration
Hi,
I tested the registration and it works fine, nice work! But it ran a bit slow on my system, so I decided to improve it. The original version took ~5.1 ms on my system. With this changes it went down to ~1 ms.
I achieved this by replacing the multi dimensional array with a one dimensional one, which is structured similar to the image data.
map[i]
is the value fordepth[i]
, etc. So you only need to walk through the array with a pointer and you are done.The new maps also include some static multiplications and additions which where computed at runtime before. There is also a new map for y image offsets, to reduce computations while runtime even more.
The rounding of the x coordinate is replaced by
const int cx = rx + 0.5f;
, which should be the same for positive numbers. But all valid x coordinates should be greater equals 0 anyway. To make sure that x is positive, a check forrx > -0.5f
is added to the if statement below.The last thing I changed is the API of the
apply
method. I changed the type for the registered image to libfreenect2::Frame, so that it is possible to check for the correct size.