Initial commit

This commit is contained in:
marek
2015-10-15 17:07:34 +02:00
parent da44352544
commit 9ebff446fa
239 changed files with 375645 additions and 0 deletions
+524
View File
@@ -0,0 +1,524 @@
// Copyright (C) 2015 Marek Kowalski (M.Kowalski@ire.pw.edu.pl), Jacek Naruniec (J.Naruniec@ire.pw.edu.pl)
// License: MIT Software License See LICENSE.txt for the full license.
// If you use this software in your research, then please use the following citation:
// Kowalski, M.; Naruniec, J.; Daniluk, M.: "LiveScan3D: A Fast and Inexpensive 3D Data
// Acquisition System for Multiple Kinect v2 Sensors". in 3D Vision (3DV), 2015 International Conference on, Lyon, France, 2015
// @INPROCEEDINGS{Kowalski15,
// author={Kowalski, M. and Naruniec, J. and Daniluk, M.},
// booktitle={3D Vision (3DV), 2015 International Conference on},
// title={LiveScan3D: A Fast and Inexpensive 3D Data Acquisition System for Multiple Kinect v2 Sensors},
// year={2015},
// }
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Windows.Forms;
using System.Globalization;
using System.Runtime.Serialization;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Timers;
namespace KinectServer
{
public partial class MainWindowForm : Form
{
[DllImport("ICP.dll")]
static extern float ICP(IntPtr verts1, IntPtr verts2, int nVerts1, int nVerts2, float[] R, float[] t, int maxIter = 200);
KinectServer oServer;
//Those three variables are shared with the OpenGLWindow class and are used to exchange data with it.
//Vertices from all of the sensors
List<float> lAllVertices = new List<float>();
//Color data from all of the sensors
List<byte> lAllColors = new List<byte>();
//Sensor poses from all of the sensors
List<AffineTransform> lAllCameraPoses = new List<AffineTransform>();
bool bServerRunning = false;
bool bRecording = false;
bool bSaving = false;
//Live view open or not
bool bLiveViewRunning = false;
System.Timers.Timer oStatusBarTimer = new System.Timers.Timer();
KinectSettings oSettings = new KinectSettings();
//The live view window class
OpenGLWindow oOpenGLWindow;
public MainWindowForm()
{
//This tries to read the settings from "settings.bin", if it failes the settings stay at default values.
try
{
IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
Stream stream = new FileStream("settings.bin", FileMode.Open, FileAccess.Read);
oSettings = (KinectSettings)formatter.Deserialize(stream);
stream.Close();
}
catch(Exception)
{
}
oServer = new KinectServer(oSettings);
oServer.eSocketListChanged += new SocketListChangedHandler(UpdateListView);
InitializeComponent();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
//The current settings are saved to a files.
IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
Stream stream = new FileStream("settings.bin", FileMode.Create, FileAccess.Write);
formatter.Serialize(stream, oSettings);
stream.Close();
oServer.StopServer();
}
//Starts the server
private void btStart_Click(object sender, EventArgs e)
{
bServerRunning = !bServerRunning;
if (bServerRunning)
{
oServer.StartServer();
btStart.Text = "Stop server";
}
else
{
oServer.StopServer();
btStart.Text = "Start server";
}
}
//Opens the settings form
private void btSettings_Click(object sender, EventArgs e)
{
SettingsForm form = new SettingsForm();
form.oSettings = oSettings;
form.oServer = oServer;
form.Show();
}
//Performs recording which is synchronized frame capture.
//The frames are downloaded from the clients and saved once recording is finished.
private void recordingWorker_DoWork(object sender, DoWorkEventArgs e)
{
oServer.ClearStoredFrames();
int nCaptured = 0;
BackgroundWorker worker = (BackgroundWorker)sender;
while (!worker.CancellationPending)
{
oServer.CaptureSynchronizedFrame();
nCaptured++;
SetStatusBarOnTimer("Captured frame " + (nCaptured).ToString() + ".", 5000);
}
}
private void recordingWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//After recording has been terminated it is time to begin saving the frames.
//Saving is downloading the frames from clients and saving them locally.
bSaving = true;
btRecord.Text = "Stop saving";
btRecord.Enabled = true;
savingWorker.RunWorkerAsync();
}
//Opens the live view window
private void OpenGLWorker_DoWork(object sender, DoWorkEventArgs e)
{
bLiveViewRunning = true;
oOpenGLWindow = new OpenGLWindow();
//The variables below are shared between this class and the OpenGLWindow.
lock (lAllVertices)
{
oOpenGLWindow.vertices = lAllVertices;
oOpenGLWindow.colors = lAllColors;
oOpenGLWindow.cameraPoses = lAllCameraPoses;
oOpenGLWindow.settings = oSettings;
}
oOpenGLWindow.Run();
}
private void OpenGLWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bLiveViewRunning = false;
updateWorker.CancelAsync();
}
private void savingWorker_DoWork(object sender, DoWorkEventArgs e)
{
int nFrames = 0;
string outDir = "out" + "\\" + txtSeqName.Text + "\\";
DirectoryInfo di = Directory.CreateDirectory(outDir);
BackgroundWorker worker = (BackgroundWorker)sender;
//This loop is running till it is either cancelled (using the btRecord button), or till there are no more stored frames.
while (!worker.CancellationPending)
{
List<List<byte>> lFrameRGBAllDevices = new List<List<byte>>();
List<List<float>> lFrameVertsAllDevices = new List<List<float>>();
oServer.GetStoredFrame(lFrameRGBAllDevices, lFrameVertsAllDevices);
nFrames++;
int nVerticesTotal = 0;
for (int i = 0; i < lFrameRGBAllDevices.Count; i++)
{
nVerticesTotal += lFrameVertsAllDevices[i].Count;
}
//This indicates that there are no more stored frames.
if (nVerticesTotal == 0)
break;
List<byte> lFrameRGB = new List<byte>();
List<Single> lFrameVerts = new List<Single>();
SetStatusBarOnTimer("Saving frame " + (nFrames).ToString() + ".", 5000);
for (int i = 0; i < lFrameRGBAllDevices.Count; i++)
{
lFrameRGB.AddRange(lFrameRGBAllDevices[i]);
lFrameVerts.AddRange(lFrameVertsAllDevices[i]);
//This is ran if the frames from each client are to be placed in separate files.
if (!oSettings.bMergeScansForSave)
{
string outputFilename = outDir + "\\" + nFrames.ToString().PadLeft(5, '0') + i.ToString() + ".ply";
saveToPly(outputFilename, lFrameVertsAllDevices[i], lFrameRGBAllDevices[i]);
}
}
//This is ran if the frames from all clients are to be placed in a single file.
if (oSettings.bMergeScansForSave)
{
string outputFilename = outDir + "\\" + nFrames.ToString().PadLeft(5, '0') + ".ply";
saveToPly(outputFilename, lFrameVerts, lFrameRGB);
}
}
}
private void savingWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
oServer.ClearStoredFrames();
bSaving = false;
//If the live view window was open, we need to restart the UpdateWorker.
if (bLiveViewRunning)
RestartUpdateWorker();
btRecord.Enabled = true;
btRecord.Text = "Start recording";
btRefineCalib.Enabled = true;
btCalibrate.Enabled = true;
}
//Continually requests frames that will be displayed in the live view window.
private void updateWorker_DoWork(object sender, DoWorkEventArgs e)
{
List<List<byte>> lFramesRGB = new List<List<byte>>();
List<List<Single>> lFramesVerts = new List<List<Single>>();
BackgroundWorker worker = (BackgroundWorker)sender;
while (!worker.CancellationPending)
{
Thread.Sleep(1);
oServer.GetLatestFrame(lFramesRGB, lFramesVerts);
//Update the vertex and color lists that are common between this class and the OpenGLWindow.
lock (lAllVertices)
{
lAllVertices.Clear();
lAllColors.Clear();
lAllCameraPoses.Clear();
for (int i = 0; i < lFramesRGB.Count; i++)
{
lAllVertices.AddRange(lFramesVerts[i]);
lAllColors.AddRange(lFramesRGB[i]);
lAllCameraPoses.Add(oServer.lCameraPoses[i]);
}
}
//Notes the fact that a new frame was downloaded, this is used to estimate the FPS.
if (oOpenGLWindow != null)
oOpenGLWindow.CloudUpdateTick();
}
}
//Performs the ICP based pose refinement.
private void refineWorker_DoWork(object sender, DoWorkEventArgs e)
{
if (oServer.bAllCalibrated == false)
{
SetStatusBarOnTimer("Not all of the devices are calibrated.", 5000);
return;
}
//Download a frame from each client.
List<List<float>> lAllFrameVertices = new List<List<float>>();
List<List<byte>> lAllFrameColors = new List<List<byte>>();
oServer.GetLatestFrame(lAllFrameColors, lAllFrameVertices);
//Initialize containers for the poses.
List<float[]> Rs = new List<float[]>();
List<float[]> Ts = new List<float[]>();
for (int i = 0; i < lAllFrameVertices.Count; i++)
{
float[] tempR = new float[9];
float[] tempT = new float[3];
for (int j = 0; j < 3; j++)
{
tempT[j] = 0;
tempR[j + j * 3] = 1;
}
Rs.Add(tempR);
Ts.Add(tempT);
}
//Use ICP to refine the sensor poses.
//This part is explained in more detail in our article (name on top of this file).
float error = 1;
float oldError = 1;
bool firstPass = true;
int iter = 0;
while (true)
{
if (iter > oSettings.nMaxNumRefineIters)
break;
error = 0;
for (int i = 0; i < lAllFrameVertices.Count; i++)
{
List<float> otherFramesVertices = new List<float>();
for (int j = 0; j < lAllFrameVertices.Count; j++)
{
if (j == i)
continue;
otherFramesVertices.AddRange(lAllFrameVertices[j]);
}
float[] verts1 = otherFramesVertices.ToArray();
float[] verts2 = lAllFrameVertices[i].ToArray();
IntPtr pVerts1 = Marshal.AllocHGlobal(otherFramesVertices.Count * sizeof(float));
IntPtr pVerts2 = Marshal.AllocHGlobal(lAllFrameVertices[i].Count * sizeof(float));
Marshal.Copy(verts1, 0, pVerts1, verts1.Length);
Marshal.Copy(verts2, 0, pVerts2, verts2.Length);
error += ICP(pVerts1, pVerts2, otherFramesVertices.Count / 3, lAllFrameVertices[i].Count / 3, Rs[i], Ts[i], oSettings.nNumICPIterations);
Marshal.Copy(pVerts2, verts2, 0, verts2.Length);
lAllFrameVertices[i].Clear();
lAllFrameVertices[i].AddRange(verts2);
}
if (firstPass)
{
firstPass = false;
}
else
{
if (Math.Abs(oldError - error) / error < 0.01)
break;
}
oldError = error;
iter++;
}
//Update the calibration data in client machines.
List<AffineTransform> worldTransforms = oServer.lWorldTransforms;
for (int i = 0; i < worldTransforms.Count; i++)
{
float[] tempT = new float[3];
float[,] tempR = new float[3, 3];
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
tempT[j] += Ts[i][k] * worldTransforms[i].R[k, j];
}
worldTransforms[i].t[j] += tempT[j];
}
for (int j = 0; j < 3; j++)
{
for (int k = 0; k < 3; k++)
{
for (int l = 0; l < 3; l++)
{
tempR[j, k] += Rs[i][l * 3 + j] * worldTransforms[i].R[l, k];
}
worldTransforms[i].R[j, k] = tempR[j, k];
}
}
}
oServer.lWorldTransforms = worldTransforms;
oServer.SendCalibrationData();
}
private void refineWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Re-enable all of the buttons after refinement.
btRefineCalib.Enabled = true;
btCalibrate.Enabled = true;
btRecord.Enabled = true;
}
//This is used for: starting/stopping the recording worker, stopping the saving worker
private void btRecord_Click(object sender, EventArgs e)
{
if (oServer.nClientCount < 1)
{
SetStatusBarOnTimer("At least one client needs to be connected for recording.", 5000);
return;
}
//If we are saving frames right now, this button stops saving.
if (bSaving)
{
btRecord.Enabled = false;
savingWorker.CancelAsync();
return;
}
bRecording = !bRecording;
if (bRecording)
{
//Stop the update worker to reduce the network usage (provides better synchronization).
updateWorker.CancelAsync();
recordingWorker.RunWorkerAsync();
btRecord.Text = "Stop recording";
btRefineCalib.Enabled = false;
btCalibrate.Enabled = false;
}
else
{
btRecord.Enabled = false;
recordingWorker.CancelAsync();
}
}
private void btCalibrate_Click(object sender, EventArgs e)
{
oServer.Calibrate();
}
private void btRefineCalib_Click(object sender, EventArgs e)
{
if (oServer.nClientCount < 2)
{
SetStatusBarOnTimer("To refine calibration you need at least 2 connected devices.", 5000);
return;
}
btRefineCalib.Enabled = false;
btCalibrate.Enabled = false;
btRecord.Enabled = false;
refineWorker.RunWorkerAsync();
}
private void saveToPly(string filename, List<Single> vertices, List<byte> colors)
{
int nVertices = vertices.Count / 3;
System.IO.StreamWriter file = new System.IO.StreamWriter(filename);
//PLY file header is written here.
file.WriteLine("ply\nformat ascii 1.0");
file.WriteLine("element vertex " + nVertices.ToString());
file.WriteLine("property float x\nproperty float y\nproperty float z\nproperty uchar red\nproperty uchar green\nproperty uchar blue\nend_header");
//Vertex and color data are written here.
for (int j = 0; j < vertices.Count / 3; j++)
{
string s = "";
for (int k = 0; k < 3; k++) s += vertices[j * 3 + k].ToString(CultureInfo.InvariantCulture) + " ";
for (int k = 0; k < 3; k++) s += colors[j * 3 + k].ToString(CultureInfo.InvariantCulture) + " ";
file.WriteLine(s);
}
file.Close();
}
void RestartUpdateWorker()
{
if (!updateWorker.IsBusy)
updateWorker.RunWorkerAsync();
}
private void btShowLive_Click(object sender, EventArgs e)
{
RestartUpdateWorker();
//Opens the live view window if it is not open yet.
if (!OpenGLWorker.IsBusy)
OpenGLWorker.RunWorkerAsync();
}
private void SetStatusBarOnTimer(string message, int milliseconds)
{
statusLabel.Text = message;
oStatusBarTimer.Stop();
oStatusBarTimer = new System.Timers.Timer();
oStatusBarTimer.Interval = milliseconds;
oStatusBarTimer.Elapsed += delegate(object sender, System.Timers.ElapsedEventArgs e)
{
oStatusBarTimer.Stop();
statusLabel.Text = "";
};
oStatusBarTimer.Start();
}
//Updates the ListBox contaning the connected clients, called by events inside KinectServer.
private void UpdateListView(List<KinectSocket> socketList)
{
List<string> listBoxItems = new List<string>();
for (int i = 0; i < socketList.Count; i++)
listBoxItems.Add(socketList[i].sSocketState);
lClientListBox.DataSource = listBoxItems;
}
}
}