mirror of
https://github.com/opencv/opencv_contrib.git
synced 2025-10-20 04:25:42 +08:00
KinectFusion demo: live input support added (#1671)
* KinFu demo: added support for live demo, some bugs fixed * minor fixes * Kinect2 workarounds and defaults added
This commit is contained in:

committed by
Vadim Pisarevsky

parent
2231018c83
commit
e351caedf7
@@ -48,6 +48,101 @@ static vector<string> readDepth(std::string fileList)
|
|||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const Size kinect2FrameSize(512, 424);
|
||||||
|
// approximate values, no guarantee to be correct
|
||||||
|
const float kinect2Focal = 366.1f;
|
||||||
|
const float kinect2Cx = 258.2f;
|
||||||
|
const float kinect2Cy = 204.f;
|
||||||
|
|
||||||
|
struct DepthSource
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DepthSource() :
|
||||||
|
depthFileList(),
|
||||||
|
frameIdx(0),
|
||||||
|
vc(),
|
||||||
|
useKinect2Workarounds(true)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
DepthSource(int cam) :
|
||||||
|
depthFileList(),
|
||||||
|
frameIdx(),
|
||||||
|
vc(VideoCaptureAPIs::CAP_OPENNI2 + cam),
|
||||||
|
useKinect2Workarounds(true)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
DepthSource(String fileListName) :
|
||||||
|
depthFileList(readDepth(fileListName)),
|
||||||
|
frameIdx(0),
|
||||||
|
vc(),
|
||||||
|
useKinect2Workarounds(true)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
Mat getDepth()
|
||||||
|
{
|
||||||
|
Mat out;
|
||||||
|
if (!vc.isOpened())
|
||||||
|
{
|
||||||
|
if (frameIdx < depthFileList.size())
|
||||||
|
out = cv::imread(depthFileList[frameIdx++], IMREAD_ANYDEPTH);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Mat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
vc.grab();
|
||||||
|
vc.retrieve(out, CAP_OPENNI_DEPTH_MAP);
|
||||||
|
|
||||||
|
// workaround for Kinect 2
|
||||||
|
if(useKinect2Workarounds)
|
||||||
|
{
|
||||||
|
out = out(Rect(Point(), kinect2FrameSize));
|
||||||
|
cv::flip(out, out, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (out.empty())
|
||||||
|
throw std::runtime_error("Matrix is empty");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty()
|
||||||
|
{
|
||||||
|
return depthFileList.empty() && !(vc.isOpened());
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateParams(KinFu::Params& params)
|
||||||
|
{
|
||||||
|
if (vc.isOpened())
|
||||||
|
{
|
||||||
|
// this should be set in according to user's depth sensor
|
||||||
|
int w = (int)vc.get(VideoCaptureProperties::CAP_PROP_FRAME_WIDTH);
|
||||||
|
int h = (int)vc.get(VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT);
|
||||||
|
|
||||||
|
float focal = (float)vc.get(CAP_OPENNI_DEPTH_GENERATOR | CAP_PROP_OPENNI_FOCAL_LENGTH);
|
||||||
|
|
||||||
|
// it's recommended to calibrate sensor to obtain its intrinsics
|
||||||
|
float fx, fy, cx, cy;
|
||||||
|
fx = fy = useKinect2Workarounds ? kinect2Focal : focal;
|
||||||
|
cx = useKinect2Workarounds ? kinect2Cx : w/2 - 0.5f;
|
||||||
|
cy = useKinect2Workarounds ? kinect2Cy : h/2 - 0.5f;
|
||||||
|
|
||||||
|
params.frameSize = useKinect2Workarounds ? kinect2FrameSize : Size(w, h);
|
||||||
|
params.intr = Matx33f(fx, 0, cx,
|
||||||
|
0, fy, cy,
|
||||||
|
0, 0, 1);
|
||||||
|
params.depthFactor = 1000.f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<string> depthFileList;
|
||||||
|
size_t frameIdx;
|
||||||
|
VideoCapture vc;
|
||||||
|
bool useKinect2Workarounds;
|
||||||
|
};
|
||||||
|
|
||||||
const std::string vizWindowName = "cloud";
|
const std::string vizWindowName = "cloud";
|
||||||
|
|
||||||
struct PauseCallbackArgs
|
struct PauseCallbackArgs
|
||||||
@@ -77,13 +172,14 @@ void pauseCallback(const viz::MouseEvent& me, void* args)
|
|||||||
static const char* keys =
|
static const char* keys =
|
||||||
{
|
{
|
||||||
"{help h usage ? | | print this message }"
|
"{help h usage ? | | print this message }"
|
||||||
"{@depth |<none>| Path to depth.txt file listing a set of depth images }"
|
"{depth | | Path to depth.txt file listing a set of depth images }"
|
||||||
|
"{camera | | Index of depth camera to be used as a depth source }"
|
||||||
"{coarse | | Run on coarse settings (fast but ugly) or on default (slow but looks better),"
|
"{coarse | | Run on coarse settings (fast but ugly) or on default (slow but looks better),"
|
||||||
" in coarse mode points and normals are displayed }"
|
" in coarse mode points and normals are displayed }"
|
||||||
};
|
};
|
||||||
|
|
||||||
static const std::string message =
|
static const std::string message =
|
||||||
"\nThis demo uses RGB-D dataset taken from"
|
"\nThis demo uses live depth input or RGB-D dataset taken from"
|
||||||
"\nhttps://vision.in.tum.de/data/datasets/rgbd-dataset"
|
"\nhttps://vision.in.tum.de/data/datasets/rgbd-dataset"
|
||||||
"\nto demonstrate KinectFusion implementation \n";
|
"\nto demonstrate KinectFusion implementation \n";
|
||||||
|
|
||||||
@@ -104,8 +200,6 @@ int main(int argc, char **argv)
|
|||||||
coarse = true;
|
coarse = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
String depthPath = parser.get<String>(0);
|
|
||||||
|
|
||||||
if(!parser.check())
|
if(!parser.check())
|
||||||
{
|
{
|
||||||
parser.printMessage();
|
parser.printMessage();
|
||||||
@@ -113,7 +207,18 @@ int main(int argc, char **argv)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
vector<string> depthFileList = readDepth(depthPath);
|
DepthSource ds;
|
||||||
|
if (parser.has("depth"))
|
||||||
|
ds = DepthSource(parser.get<String>("depth"));
|
||||||
|
if (parser.has("camera") && ds.empty())
|
||||||
|
ds = DepthSource(parser.get<int>("camera"));
|
||||||
|
|
||||||
|
if (ds.empty())
|
||||||
|
{
|
||||||
|
std::cerr << "Failed to open depth source" << std::endl;
|
||||||
|
parser.printMessage();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
KinFu::Params params;
|
KinFu::Params params;
|
||||||
if(coarse)
|
if(coarse)
|
||||||
@@ -121,9 +226,12 @@ int main(int argc, char **argv)
|
|||||||
else
|
else
|
||||||
params = KinFu::Params::defaultParams();
|
params = KinFu::Params::defaultParams();
|
||||||
|
|
||||||
// scene-specific params should be tuned for each scene individually
|
// These params can be different for each depth sensor
|
||||||
params.volumePose = params.volumePose.translate(Vec3f(0.f, 0.f, 0.5f));
|
ds.updateParams(params);
|
||||||
params.tsdf_max_weight = 16;
|
|
||||||
|
// Scene-specific params should be tuned for each scene individually
|
||||||
|
//params.volumePose = params.volumePose.translate(Vec3f(0.f, 0.f, 0.5f));
|
||||||
|
//params.tsdf_max_weight = 16;
|
||||||
|
|
||||||
KinFu kf(params);
|
KinFu kf(params);
|
||||||
|
|
||||||
@@ -135,42 +243,41 @@ int main(int argc, char **argv)
|
|||||||
Mat points;
|
Mat points;
|
||||||
Mat normals;
|
Mat normals;
|
||||||
|
|
||||||
double prevTime = getTickCount();
|
int64 prevTime = getTickCount();
|
||||||
|
|
||||||
bool pause = false;
|
bool pause = false;
|
||||||
|
|
||||||
for(size_t nFrame = 0; nFrame < depthFileList.size(); nFrame++)
|
for(Mat frame = ds.getDepth(); !frame.empty(); frame = ds.getDepth())
|
||||||
{
|
{
|
||||||
if(pause)
|
if(pause)
|
||||||
{
|
{
|
||||||
kf.getCloud(points, normals);
|
kf.getCloud(points, normals);
|
||||||
viz::WCloud cloudWidget(points, viz::Color::white());
|
if(!points.empty() && !normals.empty())
|
||||||
viz::WCloudNormals cloudNormals(points, normals, /*level*/1, /*scale*/0.05, viz::Color::gray());
|
{
|
||||||
window.showWidget("cloud", cloudWidget);
|
viz::WCloud cloudWidget(points, viz::Color::white());
|
||||||
window.showWidget("normals", cloudNormals);
|
viz::WCloudNormals cloudNormals(points, normals, /*level*/1, /*scale*/0.05, viz::Color::gray());
|
||||||
|
window.showWidget("cloud", cloudWidget);
|
||||||
|
window.showWidget("normals", cloudNormals);
|
||||||
|
|
||||||
window.showWidget("cube", viz::WCube(Vec3d::all(0),
|
window.showWidget("cube", viz::WCube(Vec3d::all(0),
|
||||||
Vec3d::all(kf.getParams().volumeSize)),
|
Vec3d::all(kf.getParams().volumeSize)),
|
||||||
kf.getParams().volumePose);
|
kf.getParams().volumePose);
|
||||||
PauseCallbackArgs pca(kf);
|
PauseCallbackArgs pca(kf);
|
||||||
window.registerMouseCallback(pauseCallback, (void*)&pca);
|
window.registerMouseCallback(pauseCallback, (void*)&pca);
|
||||||
window.showWidget("text", viz::WText(cv::String("Move camera in this window. "
|
window.showWidget("text", viz::WText(cv::String("Move camera in this window. "
|
||||||
"Close the window or press Q to resume"), Point()));
|
"Close the window or press Q to resume"), Point()));
|
||||||
window.spin();
|
window.spin();
|
||||||
window.removeWidget("text");
|
window.removeWidget("text");
|
||||||
window.registerMouseCallback(0);
|
window.registerMouseCallback(0);
|
||||||
|
}
|
||||||
|
|
||||||
pause = false;
|
pause = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Mat frame = cv::imread(depthFileList[nFrame], IMREAD_ANYDEPTH);
|
|
||||||
if(frame.empty())
|
|
||||||
throw std::runtime_error("Matrix is empty");
|
|
||||||
|
|
||||||
// 5000 for typical per-meter coefficient of PNG files
|
|
||||||
Mat cvt8;
|
Mat cvt8;
|
||||||
convertScaleAbs(frame, cvt8, 0.25/5000.*256.);
|
float depthFactor = kf.getParams().depthFactor;
|
||||||
|
convertScaleAbs(frame, cvt8, 0.25*256. / depthFactor);
|
||||||
imshow("depth", cvt8);
|
imshow("depth", cvt8);
|
||||||
|
|
||||||
if(!kf.update(frame))
|
if(!kf.update(frame))
|
||||||
@@ -183,10 +290,13 @@ int main(int argc, char **argv)
|
|||||||
if(coarse)
|
if(coarse)
|
||||||
{
|
{
|
||||||
kf.getCloud(points, normals);
|
kf.getCloud(points, normals);
|
||||||
viz::WCloud cloudWidget(points, viz::Color::white());
|
if(!points.empty() && !normals.empty())
|
||||||
viz::WCloudNormals cloudNormals(points, normals, /*level*/1, /*scale*/0.05, viz::Color::gray());
|
{
|
||||||
window.showWidget("cloud", cloudWidget);
|
viz::WCloud cloudWidget(points, viz::Color::white());
|
||||||
window.showWidget("normals", cloudNormals);
|
viz::WCloudNormals cloudNormals(points, normals, /*level*/1, /*scale*/0.05, viz::Color::gray());
|
||||||
|
window.showWidget("cloud", cloudWidget);
|
||||||
|
window.showWidget("normals", cloudNormals);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//window.showWidget("worldAxes", viz::WCoordinateSystem());
|
//window.showWidget("worldAxes", viz::WCoordinateSystem());
|
||||||
@@ -200,14 +310,15 @@ int main(int argc, char **argv)
|
|||||||
kf.render(rendered);
|
kf.render(rendered);
|
||||||
}
|
}
|
||||||
|
|
||||||
double newTime = getTickCount();
|
int64 newTime = getTickCount();
|
||||||
putText(rendered, cv::format("FPS: %2d press R to reset, P to pause, Q to quit", (int)(getTickFrequency()/(newTime - prevTime))),
|
putText(rendered, cv::format("FPS: %2d press R to reset, P to pause, Q to quit",
|
||||||
|
(int)(getTickFrequency()/(newTime - prevTime))),
|
||||||
Point(0, rendered.rows-1), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 255));
|
Point(0, rendered.rows-1), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 255));
|
||||||
prevTime = newTime;
|
prevTime = newTime;
|
||||||
|
|
||||||
imshow("render", rendered);
|
imshow("render", rendered);
|
||||||
|
|
||||||
int c = waitKey(1);
|
int c = waitKey(100);
|
||||||
switch (c)
|
switch (c)
|
||||||
{
|
{
|
||||||
case 'r':
|
case 'r':
|
||||||
|
@@ -1091,12 +1091,14 @@ void TSDFVolumeCPU::fetchPointsNormals(OutputArray _points, OutputArray _normals
|
|||||||
}
|
}
|
||||||
|
|
||||||
_points.create((int)points.size(), 1, DataType<ptype>::type);
|
_points.create((int)points.size(), 1, DataType<ptype>::type);
|
||||||
Mat((int)points.size(), 1, DataType<ptype>::type, &points[0]).copyTo(_points.getMat());
|
if(!points.empty())
|
||||||
|
Mat((int)points.size(), 1, DataType<ptype>::type, &points[0]).copyTo(_points.getMat());
|
||||||
|
|
||||||
if(_normals.needed())
|
if(_normals.needed())
|
||||||
{
|
{
|
||||||
_normals.create((int)normals.size(), 1, DataType<ptype>::type);
|
_normals.create((int)normals.size(), 1, DataType<ptype>::type);
|
||||||
Mat((int)normals.size(), 1, DataType<ptype>::type, &normals[0]).copyTo(_normals.getMat());
|
if(!normals.empty())
|
||||||
|
Mat((int)normals.size(), 1, DataType<ptype>::type, &normals[0]).copyTo(_normals.getMat());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user