Microsoft Kinect V2 + Unity 3D Depth = Warping - unity3d

I've been working on a scene in Unity3D where I have the KinectV2 depth information coming in at 512 x 424 and I'm converting that in real time to Mesh that is also 512 x 424. So there is a 1:1 ratio of pixel data (depth) and vertices (mesh).
My end goal is to make the 'Monitor 3D View' scene found in 'Microsoft Kinect Studio v2.0' with the Depth.
I've pretty much got it working in terms of the point cloud. However, there is a large amount of warping in my Unity scene. I though it might of been down to my maths, etc.
However I noticed that its the same case for the Unity Demo kinect supplied in their Development kit.
I'm just wondering if I'm missing something obvious here? Each of my pixels (or vertices in this case) is mapped out in a 1 by 1 fashion.
I'm not sure if its because I need to process the data from the DepthFrame before rendering it to scene? Or if there's some additional step I've missed out to get the true representation of my room? Because it looks like theres a slight 'spherical' effect being added right now.
These two images are a top down shot of my room. The green line represents my walls.
The left image is the Kinect in a Unity scene, and the right is within Microsoft Kinect Studio. Ignoring the colour difference, you can see that the left (Unity) is warped, whereas the right is linear and perfect.
I know it's quite hard to make out, especially that you don't know the layout of the room I'm sat in :/ Side view too. Can you see the warping on the left? Use the green lines as a reference - these are straight in the actual room, as shown correctly on the right image.
Check out my video to get a better idea:
https://www.youtube.com/watch?v=Zh2pAVQpkBM&feature=youtu.be
Code C#
Pretty simple to be honest. I'm just grabbing the depth data straight from the Kinect SDK, and placing it into a point cloud mesh on the Z axis.
//called on application start
void Start(){
_Reader = _Sensor.DepthFrameSource.OpenReader();
_Data = new ushort[_lengthInPixels];
_Sensor.Open();
}
//called once per frame
void Update(){
if(_Reader != null){
var dep_frame = _Reader.AcquireLatestFrame();
dep_frame.CopyFrameDataToArray(_Data);
dep_frame.Dispose();
dep_frame = null;
UpdateScene();
}
}
//update point cloud in scene
void UpdateScene(){
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
int index = (y * width) + x;
float depthAdjust = 0.1;
Vector3 new_pos = new Vector3(points[index].x, points[index].y, _Data[index] * depthAdjust;
points[index] = new_pos;
}
}
}
Kinect API can be found here:
https://msdn.microsoft.com/en-us/library/windowspreview.kinect.depthframe.aspx
Would appreciate any advise, thanks!

With thanks to Edward Zhang, I figured out what I was doing wrong.
It's down to me not projecting my depth points correctly, in where I need to use the CoordinateMapper to map my DepthFrame into CameraSpace.
Currently, my code assumes an orthogonal depth instead of using a perspective depth camera. I just needed to implement this:
https://msdn.microsoft.com/en-us/library/windowspreview.kinect.coordinatemapper.aspx
//called once per frame
void Update(){
if(_Reader != null){
var dep_frame = _Reader.AcquireLatestFrame();
dep_frame.CopyFrameDataToArray(_Data);
dep_frame.Dispose();
dep_frame = null;
CameraSpacePoint[] _CameraSpace = new CameraSpacePoint[_Data.Length];
_Mapper.MapDepthFrameToCameraSpace(_Data, _CameraSpace);
UpdateScene();
}
}
//update point cloud in scene
void UpdateScene(){
for(int y = 0; y < height; y++){
for(int x = 0; x < width; x++){
int index = (y * width) + x;
Vector3 new_pos = new Vector3(_CameraSpace[index].X, _CameraSpace[index].Y, _CameraSpace[index].Z;
points[index] = new_pos;
}
}
}

Related

Box2D emulation on flutter painfully slow

I am re-writing a game a made using easlejs with Box2Dweb to flutter's flame engine with the dart port of box2d. My problem is that the objects are move really really slow. Gravity setting seems to progress in a linear fashion.
I read about scale factors etc... just don't know how to link it all. The World class does not have that,
can someone show me an example of how to setup the initial screen to box2d world ratios?
I get the screenSize using flames resize override I want to use that to set the scale or whatever works.
The examples in GitHub never seem to use that and even when I download then and run them... again painfully slow falling bodies.
A simple screen with a circle or a Square falling (correctly) will be appreciated.
Here's how I instantiate the code. (I need the object to be 80x80 pixels)
class MyGame extends Game with TapDetector {
MyGame() : _world = Box2D.World.withGravity(Box2D.Vector2(0, 10)) {
_world.setAllowSleep(true);
spawnBlocks();
}
void createSquare(int index, double w, double h, double x, double y) {
int randomNumber = random.nextInt(letters.length);
var bodyDef = Box2D.BodyDef();
bodyDef.type = Box2D.BodyType.DYNAMIC;
bodyDef.position = Box2D.Vector2(x - 5, y);
bodyDef.angle = 0;
dynamicBody = _world.createBody(bodyDef);
dynamicBody.userData = letters[randomNumber];
var boxShape = Box2D.PolygonShape();
boxShape.setAsBox(w / 2, h / 2, Box2D.Vector2(w / 2, -h * 2), 0);
var boxFixtureDef = Box2D.FixtureDef();
boxFixtureDef.shape = boxShape;
boxFixtureDef.density = 0;
boxFixtureDef.restitution = 0;
boxFixtureDef.friction = 1;
dynamicBody.createFixtureFromFixtureDef(boxFixtureDef);
blocks.add(dynamicBody);
}
spawnBlocks() {
for (var i = 0; i < 8; i++) {
createSquare(
i, blockWidth, blockHeight, blockWidth * i + 18, -100 * i.toDouble());
}
}
}
It doesn't matter how high I set the gravity, the body still falls at the same speed.
Even when they hit the floor they bounce very slowly, I have used the body.setTransform to increase the position.y etc but it just seems to move right through the static body(floor).
Since the density of your square is 0 it is not affected by gravity, try to set it to something higher and see if the objects are more affected.
Don't use the body.setTransform if you don't really need to, since it will break the physics set up in the world.
Did you try this example?
https://github.com/flame-engine/flame/tree/master/doc/examples/box2d/contact_callbacks
And don't forget to add the scale argument to your world, otherwise you'll hit the speed limits quite fast since you'll be so zoomed out.
I'm the maintainer of box2d for flame (now Forge2D), if you have more questions you can join the box2d channel on our discord and I'll try to help you.

Unity C#: Line renderer from Gameobject (3D) to Canvas (Screen Space - Camera) [duplicate]

I have an image UI in a canvas with Screen Space - Camera render mode. What I like to do is move my LineRenderer to the image vertical position by looping through all the LineRenderer positions and changing its y axis. My problem is I cant get the correct position of the image that the LineRenderer can understand. I've tried using ViewportToWorldPoint and ScreenToWorldPoint but its not the same position.
Vector3 val = Camera.main.ViewportToWorldPoint(new Vector3(image.transform.position.x, image.transform.position.y, Camera.main.nearClipPlane));
for (int i = 0; i < newListOfPoints.Count; i++)
{
line.SetPosition(i, new Vector3(newListOfPoints[i].x, val.y, newListOfPoints[i].z));
}
Screenshot result using Vector3 val = Camera.main.ScreenToWorldPoint(new Vector3(image.transform.localPosition.x, image.transform.localPosition.y, -10));
The green LineRenderer is the result of changing the y position. It should be at the bottom of the square image.
Wow, this was annoying and complicated.
Here's the code I ended up with. The code in your question is the bottom half of the Update() function. The only thing I changed is what was passed into the ScreenToWorldPoint() method. That value is calculated in the upper half of the Update() function.
The RectTransformToScreenSpace() function was adapted from this Unity Answer post1 about getting the screen space coordinates of a RectTransform (which is exactly what we want in order to convert from screen space coordinates back into world space!) The only difference is that I was getting inverse Y values, so I changed from Screen.height - transform.position.y to just transform.position.y which did the trick perfectly.
After that it was just a matter of grabbing that rectangle's lower left corner, making it a Vector3 instead of a Vector2, and passing it back into ScreenToWorldPoint(). The only trick there was because of the perspective camera, I needed to know how far away the line was from the camera originally in order to maintain that same distance (otherwise the line moves up and down the screen faster than the image). For an orthographic camera, this value can be anything.
void Update () {
//the new bits:
float dist = (Camera.main.transform.position - newListOfPoints[0]).magnitude;
Rect r = RectTransformToScreenSpace((RectTransform)image.transform);
Vector3 v3 = new Vector3(r.xMin, r.yMin, dist);
//more or less original code:
Vector3 val = Camera.main.ScreenToWorldPoint(v3);
for(int i = 0; i < newListOfPoints.Count; i++) {
line.SetPosition(i, new Vector3(newListOfPoints[i].x, val.y, newListOfPoints[i].z));
}
}
//helper function:
public static Rect RectTransformToScreenSpace(RectTransform transform) {
Vector2 size = Vector2.Scale(transform.rect.size, transform.lossyScale);
Rect rect = new Rect(transform.position.x, transform.position.y, size.x, size.y);
rect.x -= (transform.pivot.x * size.x);
rect.y -= ((1.0f - transform.pivot.y) * size.y);
return rect;
}
1And finding that post from a generalized search on "how do I get the screen coordinates of a UI object" was not easy. A bunch of other posts came up and had some code, but none of it did what I wanted (including converting screen space coordinates back into world space coordinates of the UI object which was stupid easy and not reversibe, thanks RectTransformUtility!)

How to filter point clouds by creating a retangle with touches on a Tango phone

I am scanning 3D objects using tango. Now I touches 4 points on the screen to make a rectangle and then remove all point clouds outside this rectangle.
But I have checked the "Point To Point" example provided in the Tango SDK but I do not understand the relationship between point clouds and touch position on the screen. In the "TangoPointCloud.cs" file, point clouds are transformed to the world space as follows:
public void OnTangoPointCloudAvailable(TangoPointCloudData pointCloud)
{
m_mostRecentPointCloud = pointCloud;
// Calculate the time since the last successful depth data
// collection.
if (m_depthTimestamp != 0.0)
{
m_depthDeltaTime = (float)((pointCloud.m_timestamp - m_depthTimestamp) * 1000.0);
}
// Fill in the data to draw the point cloud.
m_pointsCount = pointCloud.m_numPoints;
if (m_pointsCount > 0)
{
_SetUpCameraData();
DMatrix4x4 globalTLocal;
bool globalTLocalSuccess = m_tangoApplication.GetGlobalTLocal(out globalTLocal);
if (!globalTLocalSuccess)
{
return;
}
DMatrix4x4 unityWorldTGlobal = DMatrix4x4.FromMatrix4x4(TangoSupport.UNITY_WORLD_T_START_SERVICE) * globalTLocal.Inverse;
TangoPoseData poseData;
// Query pose to transform point cloud to world coordinates, here we are using the timestamp that we get from depth.
bool poseSuccess = _GetDevicePose(pointCloud.m_timestamp, out poseData);
if (!poseSuccess)
{
return;
}
DMatrix4x4 unityWorldTDevice = unityWorldTGlobal * DMatrix4x4.TR(poseData.translation, poseData.orientation);
// The transformation matrix that represents the point cloud's pose.
// Explanation:
// The point cloud, which is in Depth camera's frame, is put in Unity world's
// coordinate system(wrt Unity world).
// Then we are extracting the position and rotation from uwTuc matrix and applying it to
// the point cloud's transform.
DMatrix4x4 unityWorldTDepthCamera = unityWorldTDevice * m_deviceTDepthCamera;
transform.position = Vector3.zero;
transform.rotation = Quaternion.identity;
// Add offset to the point cloud depending on the offset from TangoDeltaPoseController.
if (m_tangoDeltaPoseController != null)
{
m_mostRecentUnityWorldTDepthCamera = m_tangoDeltaPoseController.UnityWorldOffset * unityWorldTDepthCamera.ToMatrix4x4();
}
else
{
m_mostRecentUnityWorldTDepthCamera = unityWorldTDepthCamera.ToMatrix4x4();
}
// Converting points array to world space.
m_overallZ = 0;
for (int i = 0; i < m_pointsCount; ++i)
{
Vector3 point = pointCloud[i];
m_points[i] = m_mostRecentUnityWorldTDepthCamera.MultiplyPoint3x4(point);
m_overallZ += point.z;
}
m_overallZ = m_overallZ / m_pointsCount;
m_depthTimestamp = pointCloud.m_timestamp; //m_timestamp is the time of capture of point cloud
// For debugging
if (m_updatePointsMesh)
{
// Need to update indices too!
int[] indices = new int[m_pointsCount];
for (int i = 0; i < m_pointsCount; ++i)
{
indices[i] = i;
}
m_mesh.Clear();
m_mesh.vertices = m_points;
m_mesh.SetIndices(indices, MeshTopology.Points, 0);
}
// The color should be pose relative; we need to store enough info to go back to pose values.
m_renderer.material.SetMatrix("depthCameraTUnityWorld", m_mostRecentUnityWorldTDepthCamera.inverse);
// Try to find the floor using this set of depth points if requested.
if (m_findFloorWithDepth)
{
_FindFloorWithDepth();
}
}
else
{
m_overallZ = 0;
}
I have tried to transform the touch positions from the screen space to the world space using Camera.main.ScreenToWorldPoint(). I have also tried to transform all point clouds from the world space to the screen space. The displayed point clouds and touch positions are the same on the screen but the transformed values are completely different.
Is there anyone knows this problem? Please help me. Thank you.
In TangoPointCloud.cs, m_points is populated with the latest point cloud in Unity world space. Consequently, the simplest way to determine whether points fall within a given portion of the screen is to use Unity's Camera.WorldToViewportPoint method to transform entries in m_points into viewport space (i.e. (0,0) is bottom left, and (1,1) is top right), and then check whether the given viewport point falls within your target area.
As an example, in the PointToPoint example that you mention, you could ignore all points that fall outside of the middle rectangle (i.e. half the width, half the height) of the screen by adding a simple check to _WaitForDepth before using the next selected point:
From PointToPointGUIController._WaitForDepth:
if (pointIndex > -1)
{
Vector3 screenPoint = cam.WorldToViewportPoint(
m_pointCloud.m_points[pointIndex]);
if (screenPoint.x > 0.25f && screenPoint.x < 0.75f &&
screenPoint.y > 0.25f && screenPoint.y < 0.75f) {
// Index is valid
m_startPoint = m_endPoint;
m_endPoint = m_pointCloud.m_points[pointIndex];
m_distance = Vector3.Distance(m_startPoint, m_endPoint);
}
}
Finally, to convert from Touch.position which is in screen space (i.e. pixels) to viewport space, you can use Camera.ScreenToViewportPoint, and then you can compare these directly with the viewport coordinates of a depth point.

Orientations Offset

I am using the MVN-Unity plug-in to access Xsens motion capture data and animate a Unity character in real-time. The character in white and green and yellow is a Unity skeleton that is animated based on Xsens motion data.
I am now trying to animate a different (non-Unity) character using Xsens (the other character that looks like a human) so similar to what the plug-in does, the motion data (positions & orientations) are being mapped to his joints/bones.
But as you can see below, something is wrong with orientations...
I think the reason might be that the rotations from MVN are not properly offset. As you can see in the next two pictures, the MVN hips have the x-axis (red) pointing to the puppet's backside, whereas for the guy's hips, the x-axis points to the right of him.
It might also be that the plug-in is using global rotations somewhere where it should use local rotations. That this must be the case can be demonstrated when I rotate the guy around before I start the Unity app; i.e. select the guy's root game object and try setting the y-rotation to 0/90/180/270 before pressing play, and compare the results: every time the distortions are different.
I don't know how to properly fix this. The code snippet that updates the Unity model (mapped to the MVN puppet or the guy) is as follows. I took this from the plug-in scripts:
private void updateModel(Transform[] pose, Transform[] model)
{
// Re-set the target, then set it up based on the segments.
Vector3 pelvisPos = new Vector3();
Vector3 lastPos = target.position;
target.position = Vector3.zero;
// Map only 23 joints.
for (int i = 0; i < 23; i++)
{
switch (i)
{
// Position only on y axis, and leave x and z to the body. Apply the 'global' position & orientation to the pelvis.
case (int)XsAnimationSegment.Pelvis:
pelvisPos = pose[i].position * scale;
model[i].position = new Vector3( model[i].position.x, pelvisPos.y, model[i].position.z );
model[i].rotation = pose[i].rotation * modelRotTP[i];
break;
// Update only the 'orientation' for the rest of the segments.
default:
if ( model[i] != null )
{
model[i].rotation = pose[i].rotation * modelRotTP[i];
}
break;
}
}
// Apply root motion if the flag is enabled; i.e. true.
if (applyRootMotion)
{
// Only update x and z, since pelvis is already modified by y previously.
target.position = new Vector3(pelvisPos.x + pelvisPosTP.x, lastPos.y, pelvisPos.z + pelvisPosTP.z);
}
// Set the final rotation of the full body, but only position it to face similar as the pelvis.
Quaternion q = Quaternion.Inverse(modelRotTP[(int)XsAnimationSegment.Pelvis]) * model[(int)XsAnimationSegment.Pelvis].rotation;
target.rotation = new Quaternion(target.rotation.x, q.y, target.rotation.z, target.rotation.w);
}
I sort of understand what the code does, but I don't know how to fix this problem. Most probably to do with the axes being different? I would appreciate any help...
You can modify XsLiveAnimator.cs script in the line: 496
with that
model[segmentOrder[i]].transform.rotation = orientations[segmentOrder[i]];
model[segmentOrder[i]].transform.Rotate(rotOffset, Space.World);
rotOffset is a Vector3 of your rotation

Cocos2d / Box2d CCRibbon Collision Detection

I'm developing a game on iOS w/ cocos2d+box2d as the game engine, and am trying to add a CCRibbon (wherein the points get populated with touches), that I know how to, and to get that CCRibbon's shape linked up to box2d, so when an object collides with it (due to gravity), it bounces off as if it were a normal thing. Would anyone happen to know how to do this / give me alternatives ?
Many thanks,
Alexandre Cassagne
Take each point and create a thin static rectangular box2d polygon using the points + the adjustment to make it a shape.
for (int i = 0; i < ccribbon.points.length - 1; i++)
{
int j = i;
j++;
int width = 2;
Array ar = [];
ar[0] = new b2Vec2(ccribbon.points[i].x, ccribbon.points[i].y);
ar[1] = new b2Vec2(ccribbon.points[i].x + width, ccribbon.points[i].y + width);
ar[2] = new b2Vec2(ccribbon.points[j].x, ccribbon.points[j].y);
ar[3] = new b2Vec2(ccribbon.points[j].x + width, ccribbon.points[j].y + width);
//create new static object
b2Polygon b2p = new b2Polygon();
b2p.setAsArray(ar);
//do rest to add it to world etc.
}
of course don't copy that code exactly its just from what i remember and i'm also sure its a combination of C# and Actionscript 3. its kindof a not so pseudo code with lots of blanks you'll need to fill in. Why the comments are there :P.
Thats basically how i would do it though. My experience is only in box2d for flash though.
Have you read this....http://www.raywenderlich.com/606/how-to-use-box2d-for-just-collision-detection-with-cocos2d-iphone