Calculate BPM from Kinect sensor data - c#

I am struggeling with the Kinect for Windows SDK to create an application for conducting (with C#).
Basically I need to track one hand (usually the right one) of a conductor and recognize his speed in directing (BPM) to send this value to another application via MIDI.
What I started with is the SkeletonFramesReadyEvent adding the JointType.HandRight with a DateTime.Now.Ticks timestamp to a history List which is updated and removes the first entry. I keep the history of 60 frames (2 seconds).
I calculate the BPM by searching for the last low and high of the Joint.Position.Y and then calculate the difference and divide bpm = 60*ticksPerSecond/diff. However the result is wrong. Is there another way of doing this? What am I missing?
This is what I am using so far:
public int DetectBPM(JointType type)
{
// we have not history yet
if (!HasHistory()) return 0;
// only calculate every second
var detectTime = DateTime.Now.Second;
if (_lastBPM != 0 && _lastBPMDectect == detectTime) return _lastBPM;
// search last high/low boundaries
var index = (int) type;
var list = History[index];
var i = list.Count - 1;
var lastHigh = list[i];
var lastLow = list[i];
// shift to last peak first
while (i > 0 && list[i].Joint.Position.Y >= list[i - 1].Joint.Position.Y) i--;
// find last low
while (i >= 0 && lastLow.Joint.Position.Y >= list[i].Joint.Position.Y) lastLow = list[i--];
// find last high
while (i >= 0 && lastHigh.Joint.Position.Y <= list[i].Joint.Position.Y) lastHigh = list[i--];
var ticks = lastLow.Timestamp - lastHigh.Timestamp;
var elapsedTime = new TimeSpan(ticks);
var bpm = (int) (60000/elapsedTime.TotalMilliseconds);
Console.WriteLine("DEBUG: BPM = " + _lastBPM + ", elapsedMS: " + elapsedTime.TotalMilliseconds);
_lastBPMDectect = detectTime;
_lastBPM = bpm;
return _lastBPM;
}

I figured out how to do it. I was missing a point and calculated the BPM between a peak and a low position of the hand which is wrong. I have to calucalte the time difference between the last two low points to get the correct result.
The correct way to go is, find the stating point which is the last peak. From there move to the last low, this is the first point to calculate the difference from. The move to the next peak and go down to the next low again which is the second point to calculate the difference from.
The principle is shown in the figure below
And that results in a nicely BPM calculated like this:
var ticks = Math.Abs(firstLow.Ticks - secondLow.Ticks);
var elapsedTime = new TimeSpan(ticks);
var bpm = (int) (60000/elapsedTime.TotalMilliseconds);
Thanks for participating anyway.

Related

How to implement threading in Xamarin.Android?

I'm trying to send/publish at 100ms, and the message looks like this
x.x.x.x.x.x.x.x.x.x
So every 100ms or so subscribe will be called. My problem is that I think, it's not fast enough, (i.e if the current subscribe is not yet done and another subscribe is being called/message being published)
I was thinking, on how could I keep on populating the list, at the same time graph with Oxyplot. Can I use threading for this?
var x = 0;
channel.Subscribe(message =>
{
this.RunOnUiThread(() =>
{
var sample = message.Data;
byte[] data = (byte[])sample;
var data1 = System.Text.Encoding.ASCII.GetString(data);
var splitData = data1.Split('-');
foreach(string s in splitData) //Contains 10
{
double y = double.Parse(s);
y /= 100;
series1.Points.Add(new DataPoint(x, y));
MyModel.InvalidatePlot(true);
x++;
}
if (x >= xaxis.Maximum)
{
xaxis.Pan(xaxis.Transform(-1 + xaxis.Offset));
}
});
});
Guaranteeing a minimum execution time goes into Realtime Programming. And with a App on a Smartphone OS you are about as far from that I can imagine you to be. The only thing farther "off" would be any interpreted langauge (PHP, Python).
The only thing you can do is define a minimum time between itterations. I did once wrote some example code for doing that from within a alternative thread. A basic rate limiting code:
integer interval = 20;
DateTime dueTime = DateTime.Now.AddMillisconds(interval);
while(true){
if(DateTime.Now >= dueTime){
//insert code here
//Update next dueTime
dueTime = DateTime.Now.AddMillisconds(interval);
}
else{
//Just yield to not tax out the CPU
Thread.Sleep(1);
}
}
Note that DateTime.Now only resturns something new about every 18 ms, so anything less then 20 would be too little.
If you think you can not afford a minimum time, you may need to read the Speed Rant.

Real time filtering of heart rate data

I'm trying to build a heart rate monitor, which I intend to code a mobile application around. Currently, I'm receiving the data in my application. Heres an example of the data:
However, I really would like to be able to remove the static element below the pulsating curve through some sort of high pass filter. Currently, I have the following code:
//HIGH PASS
//Fetch the next measurement item
j = float.Parse(Encoding.Default.GetString(e.Characteristic.Value));
counter++;
total += j;
if (counter % 5 == 0)
{
avg = total / counter;
total = 0;
counter = 0;
}
if (j < h || h > avg && h >= 0)
{
h = j;
}
else if (h <= 0) //Initially h is 0
{
h = j;
}
float g = j-(h-5); //This is the output
The code does to some degree work. What it does, is that it monitors the data and sets h=j if a lower value is present (by doing so, I constantly have the lowest point on the curve, which I can then remove).
Furthermore, for every 5 data points, a new average is put. Primarily to constantly check whether the filter is set too high. (i have roughly 6 data points every second)
The code is not really optimal. One of the major flaws is, if I remove my finger from the Heart rate sensor (j is being significantly lower), meaning that the curve will have almost no HR sine curve.
I could potentially add h=avg; to the counter if-statement. But that would cause the curve to constantly flicker up and down.
What are your suggestion guys? how can I tweak this?

c# Normalized power for Polar cycle

List<int> NPower = new List<int>();
List<double> list = new List<double>();
try
{
for (int i = 1; i < dataGridView1.Rows.Count; i++)
{
for (int n = 0; n < i + 30; n++)
{
NPower.Add(Convert.ToInt32(dataGridView1.Rows[i + n].Cells[6].Value));
}
}
average = NPower.Average();
total = Math.Pow(average, 4);
NPower.Clear();
}
catch (Exception)
{
average = NPower.Average();
NP = Convert.ToInt32(Math.Pow(average, (1.0 / 3.0)));
label19.Text = "Normalised Power: " + NP.ToString();
NPower.Clear();
}
Hi so i'm trying to calculate the normalized power for a cycling polar cycle. I know that for the normalized power you need to:
1) starting at the 30 s mark, calculate a rolling 30 s average (of the preceeding time points, obviously).
2) raise all the values obtained in step #1 to the 4th power.
3) take the average of all of the values obtained in step #2.
4) take the 4th root of the value obtained in step #3.
I think i have done that but the normalized power comes up with 16 which isnt correct. Could anyone look at my code to see if they could figure out a solution. Thankyou, sorry for my code i'm still quite new to this so my code might be in the incorrect format.
I'm not sure that I understand your requirements or code completely, but a few things I noticed:
Since you're supposed to start taking the rolling average after 30 seconds, shouldn't i be initialized to 30 instead of 1?
Since it's a rolling average, shouldn't n be initialized to the value of i instead of 0?
Why is the final result calculated inside a catch block?
Shouldn't it be Math.Pow(average, (1.0 / 4.0)) since you want the fourth root, not the third?

Making zooms shortcuts in SciChart WPF

1) I want to create zoom shortcuts- 5 minutes before.
10 minutes
hour and 24 hours from the lasttick.
I made this code and it doesnt work right.
what should i fix?
ZoomOptions = new List<ZoomOption>
{
new ZoomOption("5M", TimeSpan.FromMinutes(5)),
new ZoomOption("30M", TimeSpan.FromMinutes(30)),
new ZoomOption("1H", TimeSpan.FromHours(1)),
new ZoomOption("1D", TimeSpan.FromHours(24)),
};
SelectedZoomOption = ZoomOptions.Last();
private void UpdateZoom()
{
if (_viewModel == null ||
_viewModel.SelectedZoomOption == null ||
_viewModel.LastTick == null) return;
var timeSpan = _viewModel.SelectedZoomOption.Time;
var latestXValue = _viewModel.LastTick.Time;
var startDate = latestXValue - timeSpan;
var axis = (CategoryDateTimeAxis)Chart.XAxis;
if (axis == null || axis.VisibleRange == null) return;
var calc = (ICategoryCoordinateCalculator)axis.GetCurrentCoordinateCalculator();
if (calc == null) return;
var startIndex = calc.TransformDataToIndex(startDate);
var max = ((IndexRange)axis.VisibleRange).Max;
var desiredMax = calc.TransformDataToIndex(latestXValue) + 5;
if (timeSpan < TimeSpan.FromMinutes(10))
{
max = desiredMax;
}
else if (max == desiredMax)
{
max += 100;
}
axis.VisibleRange = new IndexRange(startIndex, max);
}
2) why when i dont have graph history but only new ticks i cant see the graph from beginning but should go back a little bit with the mouse to before?
3) what should i do when changing graphs in menu to initialize and reset?
In SciChart when you are using the CategoryDateTimeAxis, one bar (one data-point) = x Minutes (assuming your data is equal spacing per bar).
For instance, if you have 15-minute (900 seconds) bars on the chart, then 10 bars = 150 minutes (9000 seconds). Therefore all you need to do is to set XAxis.VisibleRange as follows:
public void SetRangeToTimeFrame(TimeSpan timeFrame)
{
// Assuming you have a reference to the data series of type OhlcDataSeries
int indexMax = dataSeries.XValues.Count - 1;
// Assuming you know timeframe (one bar = X seconds)
// If you don;t know this, SciChart tries to calculate it in the
// CategoryDateTimeAxis.BarTimeFrame property
const int timeframe = 900; // for example 900 seconds per bar
// Calculate the min index to display. Do not go below zero
int indexMin = Math.Max(indexMax - (timeFrame.TotalSeconds / timeFrame), 0);
// Set XAxis.VisibleRange equal to the new range
// OR, Set via binding if you have a property XVisibleRange in ViewModel
XAxis.VisibleRange = new IndexRange(indexMin, indexMax);
}
That really is it!
If you have variable length bars (e.g. one bar is 10 minutes, one is 20 seconds) then your calculation becomes near impossible ... but it is still possible. Let me know if this is the case, I will improve the answer.
(2) and (3) are really different questions and need more clarification before they can be answered.
Disclaimer: I am the tech lead of the SciChart WPF Project

Compare each element in an array to each other

I need to compare a 1-dimensional array, in that I need to compare each element of the array with each other element. The array contains a list of strings sorted from longest to the shortest. No 2 items in the array are equal however there will be items with the same length. Currently I am making N*(N+1)/2 comparisons (127.8 Billion) and I'm trying to reduce the number of over all comparisons.
I have implemented a feature that basically says: If the strings are different in length by more than x percent then don't bother they not equal, AND the other guys below him aren't equal either so just break the loop and move on to the next element.
I am currently trying to further reduce this by saying that: If element A matches element C and D then it stands to reason that elements C and D would also match so don't bother checking them (i.e. skip that operation). This is as far as I've factored since I don't currently know of a data structure that will allow me to do that.
The question here is: Does anyone know of such a data structure? or Does anyone know how I can further reduce my comparisons?
My current implementation is estimated to take 3.5 days to complete in a time window of 10 hours (i.e. it's too long) and my only options left are either to reduce the execution time, which may or may not be possible, or distrubute the workload accross dozens of systems, which may not be practical.
Update: My bad. Replace the word equal with closely matches with. I'm calculating the Levenstein distance
The idea is to find out if there are other strings in the array which closely matches with each element in the array. The output is a database mapping of the strings that were closely related.
Here is the partial code from the method. Prior to executing this code block there is code that loads items into the datbase.
public static void RelatedAddressCompute() {
TableWipe("RelatedAddress");
decimal _requiredDistance = Properties.Settings.Default.LevenshteinDistance;
SqlConnection _connection = new SqlConnection(Properties.Settings.Default.AML_STORE);
_connection.Open();
string _cacheFilter = "LevenshteinCache NOT IN ('','SAMEASABOVE','SAME')";
SqlCommand _dataCommand = new SqlCommand(#"
SELECT
COUNT(DISTINCT LevenshteinCache)
FROM
Address
WHERE
" + _cacheFilter + #"
AND
LEN(LevenshteinCache) > 12", _connection);
_dataCommand.CommandTimeout = 0;
int _addressCount = (int)_dataCommand.ExecuteScalar();
_dataCommand = new SqlCommand(#"
SELECT
Data.LevenshteinCache,
Data.CacheCount
FROM
(SELECT
DISTINCT LevenshteinCache,
COUNT(LevenshteinCache) AS CacheCount
FROM
Address
WHERE
" + _cacheFilter + #"
GROUP BY
LevenshteinCache) Data
WHERE
LEN(LevenshteinCache) > 12
ORDER BY
LEN(LevenshteinCache) DESC", _connection);
_dataCommand.CommandTimeout = 0;
SqlDataReader _addressReader = _dataCommand.ExecuteReader();
string[] _addresses = new string[_addressCount + 1];
int[] _addressInstance = new int[_addressCount + 1];
int _itemIndex = 1;
while (_addressReader.Read()) {
string _address = (string)_addressReader[0];
int _count = (int)_addressReader[1];
_addresses[_itemIndex] = _address;
_addressInstance[_itemIndex] = _count;
_itemIndex++;
}
_addressReader.Close();
decimal _comparasionsMade = 0;
decimal _comparisionsAttempted = 0;
decimal _comparisionsExpected = (decimal)_addressCount * ((decimal)_addressCount + 1) / 2;
decimal _percentCompleted = 0;
DateTime _startTime = DateTime.Now;
Parallel.For(1, _addressCount, delegate(int i) {
for (int _index = i + 1; _index <= _addressCount; _index++) {
_comparisionsAttempted++;
decimal _percent = _addresses[i].Length < _addresses[_index].Length ? (decimal)_addresses[i].Length / (decimal)_addresses[_index].Length : (decimal)_addresses[_index].Length / (decimal)_addresses[i].Length;
if (_percent < _requiredDistance) {
decimal _difference = new Levenshtein().threasholdiLD(_addresses[i], _addresses[_index], 50);
_comparasionsMade++;
if (_difference <= _requiredDistance) {
InsertRelatedAddress(ref _connection, _addresses[i], _addresses[_index], _difference);
}
}
else {
_comparisionsAttempted += _addressCount - _index;
break;
}
}
if (_addressInstance[i] > 1 && _addressInstance[i] < 31) {
InsertRelatedAddress(ref _connection, _addresses[i], _addresses[i], 0);
}
_percentCompleted = (_comparisionsAttempted / _comparisionsExpected) * 100M;
TimeSpan _estimatedDuration = new TimeSpan((long)((((decimal)(DateTime.Now - _startTime).Ticks) / _percentCompleted) * 100));
TimeSpan _timeRemaining = _estimatedDuration - (DateTime.Now - _startTime);
string _timeRemains = _timeRemaining.ToString();
});
}
InsertRelatedAddress is a function that updates the database, and there are 500,000 items in the array.
OK. With the updated question, I think it makes more sense. You want to find pairs of strings with a Levenshtein Distance less than a preset distance. I think the key is that you don't compare every set of strings and rely on the properties of Levenshtein distance to search for strings within your preset limit. The answer involves computing the tree of possible changes. That is, compute possible changes to a given string with distance < n and see if any of those strings are in your set. I supposed this is only faster if n is small.
It looks like the question posted here: Finding closest neighbour using optimized Levenshtein Algorithm.
More info required. What is your desired outcome? Are you trying to get a count of all unique strings? You state that you want to see if 2 strings are equal and that if 'they are different in length by x percent then don't bother they not equal'. Why are you checking with a constraint on length by x percent? If you're checking for them to be equal they must be the same length.
I suspect you are trying to something slightly different to determining an exact match in which case I need more info.
Thanks
Neil

Categories

Resources