I have two Intel RealSense Cameras capturing and recording data. I am capturing frames for awhile and then taking the data and updating the UI with it, but it comes in spurts. I want to have a seemingly continuous flow of data coming in.
I've tried by making an asynchronous event that goes through the frames and updates as seen below:
private async void ButtonStart_Click(object sender, EventArgs e)
{
ButtonStart.Enabled = false;
try
{
Context context = new Context();
// var dev = context.QueryDevices();
//Declare RealSense pipeline, encapsulating the actual device and sensors
/*
Pipeline pipe = new Pipeline();
//create a config for the pipeline
var cfg = new Config();
//add the T265 Pose stream
cfg.EnableStream(Stream.Pose);
//start the stream
var pp = pipe.Start(cfg);
*/
Config config = new Config();
var dev = ctx.QueryDevices();
int i = 0;
foreach (var device in dev)
{
config.EnableDevice(dev[i].Info[CameraInfo.SerialNumber]);
config.EnableAllStreams();
Pipeline p = new Pipeline(ctx);
p.Start(config);
pipelines[i] = p;
i++;
}
//Pipeline pipe2 = new Pipeline();
//var cfg2 = new Config();
//cfg2.EnableStream(Stream.Color);
//cfg2.EnableStream(Stream.Depth);
// var pp2 = pipe2.Start(cfg2);
//variables for direction change
float lastX = -300;
float lastY = -300;
float lastZ = -300;
string dx = "";
string dy = "";
string dz = "";
//main loop for frames
await Task.Run(() =>
{
while (true)
{
using (var frames = pipelines[1].WaitForFrames())
{
var f = frames.FirstOrDefault(Stream.Pose);
var pose_data = f.As<PoseFrame>().PoseData;
if (lastX != -300)
{
//x
if (lastX > pose_data.translation.x)
{
dx = "LEFT";
}
else if (lastX < pose_data.translation.x)
{
dx = "RIGHT";
}
else
{
dx = "";
}
//y
if (lastY > pose_data.translation.y)
{
dy = "DOWN";
}
else if (lastY < pose_data.translation.y)
{
dy = "UP";
}
else
{
dy = "";
}
//z
if (lastZ > pose_data.translation.z)
{
dz = "FORWARD";
}
else if (lastZ < pose_data.translation.z)
{
dz = "BACKWARD";
}
else
{
dz = "";
}
}
//update the UI
UpdateUI(pose_data.translation.x.ToString(), pose_data.translation.y.ToString(), pose_data.translation.z.ToString(), dx, dy, dz);
//Set last x,y,z to see what change occurred
lastX = pose_data.translation.x;
lastY = pose_data.translation.y;
lastZ = pose_data.translation.z;
dx = "";
dy = "";
dz = "";
}
}
});
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
} //end catch
ButtonStart.Enabled = true;
}
public void UpdateUI(string x, string y, string z, string dx, string dy, string dz)
{
var timeNow = DateTime.Now;
if ((DateTime.Now - previousTime).Milliseconds <= 15) return;
//object o = new object();
//label for x
synchronizationContext.Post(new SendOrPostCallback(o =>
{
LabelXPosition.Text = o.ToString();
}), x);
//label for y
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelYPosition.Text = o.ToString();
}), y);
//label for z
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelZPosition.Text = o.ToString();
}), z);
//label for dx
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelLR.Text = o.ToString();
}), dx);
//label for dy
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelUD.Text = o.ToString();
}), dy);
//label for dz
synchronizationContext.Post(new SendOrPostCallback(o =>
{
//Set the labels for x,y,z
LabelFB.Text = o.ToString();
}), dz);
previousTime = timeNow;
}
Is there any way I can stop it from "freezing" up while reporting the data?
If device initialization does not need synchronization context then just include entire try body into a task:
private async void ButtonStart_Click(object sender, EventArgs e) {
ButtonStart.Enabled = false;
try
{
await Task.Run(() => {
Context context = new Context();
Config config = new Config();
...
}
} catch {
...
}
}
Initialization will be dispatched in the thread pool thread and UI won't freeze. Please note that creating methods that are async
and return void
is a bad practice , event handler is an exception though.
Inside of UpdateUI
you dispatch 6 messages to synchronization context to update 6 labels. You can group everything to a single update:
synchronizationContext.Post(_ => {
LabelXPosition.Text = x.ToString();
LabelYPosition.Text = y.ToString()
...
}, null));
There is a small overhead on capturing your variable to closure, however, you have less events dispatched in the UI thread and also you don't box all of them by casting to an object
in Post
method (second argument).
DateTime.Now
is rather expensive operation as it converts time to current timezone. DateTime.UtcNow
is more lightweight. However, for your purposes I think Stopwatch will be much more suitable.
There are 2 concerns here: too small time and functional issue in the algorithm.
You don't update UI if time between frames is less than 15ms:
if ((DateTime.Now - previousTime).Milliseconds <= 15) return;
Please note, that 15ms is a rather small value and I bet none user will be able to handle such refresh rate in your labels :). I think even 150ms will be too fast, so please consider a bigger value.
Also your implementation has functional issue because you drop UI dispatch inside UpdateUI
, so you may report incorrect data to user, for example:
1. t=0; x=0; lastX=0
2. t=15; x=100; label=Right; lastX=100
3. t=20; x=300; <don't update>; lastX=300
4. t=30; x=250; label=Left; lastX=250
You reported to user that Pose moved to the Right
when x=100
and then reported that it moved Left
when x=250
what is not correct.
I'd recommend to move throttling logic to the loop so that you don't have this issue:
val sw = StopWatch.StartNew()
using (var frames = pipelines[1].WaitForFrames())
{
if(sw.ElapsedMilliseconds > 150) continue;
...
sw.Restart()
}
You are using push model by dispatching messages in your synchronization context. Instead you can pass these details through the state and update UI using Timer
:
// Form fields:
Vector3 _currentLocation;
Vector3 _dispatchedLocation;
System.Windows.Forms.Timer _poseTimer;
private void Form1_Load(object sender, EventArgs e) {
_poseTimer = new System.Windows.Forms.Timer(this.Container){Interval=150};
_poseTimer.Tick += displayPoseChange;
}
private void displayPoseChange(object sender, EventArgs e)
{
// compare _currentLocation to _dispatchedLocation
// update labels
// replace _dispatchedLocation with _currentLocation
}
// ButtonClick method
poseTimer.Start()
...
using (var frames = pipelines[1].WaitForFrames()) {
_currentLocation = pose_data.translation // just update the field
...
}
...
poseTimer.Stop()
...
You are processing frames from the pipeline and there is a great framework that could be very handy for that: Reactive Extensions
var pipeline = await initializePipeline();
var obs = Observable.FromEvent(
callback => pipeline.Start(callback),
_ => pipeline.Stop());
obs.Where(Stream.Pose) // get only Poses
.Select(f => f.As<PoseFrame>().PoseData) // select only what we need
.Where(pose_data => pose_data.translation.x != -300) // filter out?
.Sample(TimeSpan.FromMilliseconds(100)) // throttle the UI
.Buffer(2, 1) // gives you last and current items
.ObserveOn(this) // dispatch on the UI
.Subscribe(data => {var last=data[0]; var current=data[1]; /* update labels*/});
await obs.GetAwaiter();
Code above shows you the idea but I didn't try to compile it, so you might need to alight types.
More documentation and examples .
You can use Background Worker in order to do the process without freezing the form. If the concern is on the speed of the process execution and it involves a loop, you can use Task-Parallel Library
Example is Parallel.Foreach
Parallel.ForEach(dev, device=> {
config.EnableDevice(dev[i].Info[CameraInfo.SerialNumber]);
config.EnableAllStreams();
Pipeline p = new Pipeline(ctx);
p.Start(config);
pipelines[i] = p;
i++;;
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.