public PointAnalysis(Bout bout, IppiPoint coordinate, IppiPoint_32f smoothenedCoordinate, float instantSpeed) { CompletedBout = bout; OriginalCoordinate = coordinate; SmoothenedCoordinate = smoothenedCoordinate; InstantSpeed = instantSpeed; }
/// <summary> /// Detects bouts and returns summary information /// </summary> /// <param name="isb">The instant speed trace</param> /// <param name="speedThreshold">Speeds below this threshold will be set to 0</param> /// <param name="minFramesPerBout">For a movement to be called a bout we require at least this number of frames</param> /// <param name="maxFramesAtPeak">Bouts should have clearly defined peaks. Bouts with a peak longer than this number get rejected</param> /// <param name="frameRate">The framerate used for acquisition</param> /// <returns>A bout summary info structure</returns> public BoutSummary AnalyzeBouts(InstantSpeedBuffer isb, float speedThreshold, int minFramesPerBout, int maxFramesAtPeak, int frameRate) { if (IsDisposed) { throw new ObjectDisposedException(this.ToString()); } if (_isCalc == null) { _isCalc = new InstantSpeedBuffer(isb.Size.width); } else if (_isCalc.Size.width != isb.Size.width) { _isCalc.Dispose(); _isCalc = new InstantSpeedBuffer(isb.Size.width); } BoutSummary retval = new BoutSummary(); Bout b = new Bout(); int i = 0; //threshold the speed trace - copy result into our calculation buffer ip.ippiThreshold_LTVal_32f_C1R(isb.Buffer, isb.Stride, _isCalc.Buffer, _isCalc.Stride, isb.Size, speedThreshold, 0); while (i < _isCalc.Size.width) { if (_isCalc[i] == 0) { i++; continue; } else//found potential bout start since speed above threshold { int peaklength = 0; b.BoutStart = i; b.PeakSpeed = _isCalc[i]; b.BoutPeak = i; b.TotalDisplacement = _isCalc[i] / frameRate; //loop over following frames collecting all frames that belong to the bout while (i < _isCalc.Size.width && _isCalc[i] > 0) { if (_isCalc[i] > b.PeakSpeed) { b.PeakSpeed = _isCalc[i]; b.BoutPeak = i; peaklength = 0; //new peakspeed found, reset peaklength } else if (_isCalc[i] == b.PeakSpeed) //another frame with the same speed as our peak { peaklength++; } b.TotalDisplacement += _isCalc[i] / frameRate; i++; } b.BoutEnd = i - 1; //check our bout criteria if ((b.BoutEnd - b.BoutStart + 1) >= minFramesPerBout && peaklength <= maxFramesAtPeak) { //we have a valid bout if (b.BoutStart == b.BoutPeak) { b.BoutStart--; } retval.NumberOfBouts++; retval.AverageDisplacement += b.TotalDisplacement; retval.AveragePeakSpeed += b.PeakSpeed; retval.AverageLength += (b.BoutEnd - b.BoutStart + 1); } }//found potential bout } retval.AverageDisplacement /= retval.NumberOfBouts; retval.AveragePeakSpeed /= retval.NumberOfBouts; retval.AverageLength /= retval.NumberOfBouts; return(retval); }
/// <summary> /// Processes the next point, filtering its position /// computing speeds and detecting bouts /// </summary> /// <param name="coordinate">The coordinate to analyze</param> /// <returns>The analysis of the given coordinate</returns> public PointAnalysis ProcessNextPoint(IppiPoint coordinate) { //set up locals for processing float x = coordinate.x; float y = coordinate.y; float xSmooth, ySmooth, speed; xSmooth = ySmooth = speed = 0; //optional filtered speed float spdSmooth = 0; //filter x-coordinate sp.ippsFIR_32f(&x, &xSmooth, 1, _filterStateX); //filter y-coordinate sp.ippsFIR_32f(&y, &ySmooth, 1, _filterStateY); //compute instant speed speed = (float)Math.Sqrt((xSmooth - _lastX) * (xSmooth - _lastX) + (ySmooth - _lastY) * (ySmooth - _lastY)); speed *= 240; //if requested, also filter the instant-speed value if (_spdTapLength > 0) { sp.ippsFIR_32f(&speed, &spdSmooth, 1, _filterStateSpd); } else { spdSmooth = speed; } //update last coordinates _lastX = xSmooth; _lastY = ySmooth; //Bout detection if (_boutDetectionState.InPutativeBout) //we are currently adding frames to a new putative bout { if (spdSmooth < _speedThreshold) //putative bout is finished { _boutDetectionState.InPutativeBout = false; //reset if (_boutDetectionState.FramesInBout >= _minFramesPerBout && _boutDetectionState.FramesAtPeak <= _maxFramesAtPeak) //bout was valid, fill bout structure and return it { Bout bout = new Bout(); bout.BoutEnd = _index - 1; bout.BoutStart = _boutDetectionState.StartFrame; bout.BoutPeak = _boutDetectionState.PeakFrame; if (bout.BoutPeak == bout.BoutStart) { bout.BoutStart--;//don't start bout on peakframe } bout.PeakSpeed = _boutDetectionState.PeakSpeed; bout.TotalDisplacement = _boutDetectionState.TotalDisplacement; bout.StartingCoordinate = _boutDetectionState.StartingCoordinate; bout.FinishCoordinate = new IppiPoint_32f(xSmooth, ySmooth); //don't forget to update the index before we return _index++; return(new PointAnalysis(bout, coordinate, new IppiPoint_32f(xSmooth, ySmooth), spdSmooth)); } } else//putative bout continues { _boutDetectionState.FramesInBout++; _boutDetectionState.TotalDisplacement += spdSmooth / _frameRate; if (spdSmooth > _boutDetectionState.PeakSpeed)//new peak-speed detected { _boutDetectionState.PeakSpeed = spdSmooth; _boutDetectionState.PeakFrame = _index; _boutDetectionState.FramesAtPeak = 1; //reset } else if (spdSmooth == _boutDetectionState.PeakSpeed) //maybe not the best criterion - equality of float values very unlikely { _boutDetectionState.FramesAtPeak++; } } } else//currently the fish is stationary { if (spdSmooth > _speedThreshold)//we should start a new putative bout { _boutDetectionState.StartBout(spdSmooth, _index, _frameRate); _boutDetectionState.StartingCoordinate.x = xSmooth; _boutDetectionState.StartingCoordinate.y = ySmooth; } } //advance index _index++; //return our analysis return(new PointAnalysis(coordinate, new IppiPoint_32f(xSmooth, ySmooth), spdSmooth)); }