public TrackFrame(XmlReader r, PointF scale, TimestampMapper timestampMapper) { bool isEmpty = r.IsEmptyElement; long time = 0; PositionningSource source = PositionningSource.Manual; PointF location = PointF.Empty; if (r.MoveToAttribute("time")) { time = timestampMapper(r.ReadContentAsLong()); } if (r.MoveToAttribute("source")) { source = (PositionningSource)Enum.Parse(typeof(PositionningSource), r.ReadContentAsString()); } if (r.MoveToAttribute("location")) { location = XmlHelper.ParsePointF(r.ReadContentAsString()); location = location.Scale(scale.X, scale.Y); } r.ReadStartElement(); if (!isEmpty) { r.ReadEndElement(); } this.time = time; this.location = location; this.positionningSource = source; }
public TrackFrame(long time, PointF location, Bitmap template, PositionningSource positionningSource) { this.time = time; this.location = location; this.template = template; this.positionningSource = positionningSource; }
/// <summary> /// Creates a timeline entry (TrackFrame) from an existing location. /// Does not perform any tracking. /// Extracts the pattern from the image. /// </summary> private TrackFrame CreateTrackFrame(PointF location, PositionningSource positionningSource) { Rectangle region = location.Box(trackerParameters.BlockWindow).ToRectangle(); Bitmap template = context.Image.ExtractTemplate(region); return(new TrackFrame(context.Time, location, template, positionningSource)); }
private void WriteTrackablePoint(XmlTextWriter w, int frameCount, long time) { PointF nonTrackingValue = random.NextPointF(0, imageSize.Width, 0, imageSize.Height); PointF currentValue = random.NextPointF(0, imageSize.Width, 0, imageSize.Height); WriteTrackerParameters(w); w.WriteElementString("NonTrackingValue", XmlHelper.WritePointF(nonTrackingValue)); w.WriteElementString("CurrentValue", XmlHelper.WritePointF(currentValue)); w.WriteStartElement("Timeline"); List <PointF> points = GetRandomTrajectory(frameCount); for (int i = 0; i < frameCount; i++) { Array values = Enum.GetValues(typeof(PositionningSource)); PositionningSource source = (PositionningSource)values.GetValue(random.Next(values.Length)); long currentTime = time + (i * averageTimestampPerFrame); w.WriteStartElement("Frame"); w.WriteAttributeString("time", currentTime.ToString()); w.WriteAttributeString("location", XmlHelper.WritePointF(points[i])); w.WriteAttributeString("source", source.ToString()); w.WriteEndElement(); } w.WriteEndElement(); }
/// <summary> /// Track the point in the current image, or use the existing data if already known. /// We do this even if the drawing is currently not tracking, to push the existing tracked data in the object. /// Important: for drawings containing multiple trackable points, either all or none of them should have a new value. /// If some of them successfully track and some other don't, the one that didn't must insert the closest frame value. /// This way we ensure the timelines are always of the same length. /// </summary> /// <param name="context"></param> public bool Track(TrackingContext context) { bool inserted = false; this.context = context; if (!trackTimeline.HasData()) { // Not a single entry in the timeline. // This drawing has never been activated for tracking so far. currentValue = nonTrackingValue; timeDifference = -1; if (isTracking) { // Use the current user-set position as a first tracked point. trackTimeline.Insert(context.Time, CreateTrackFrame(currentValue, PositionningSource.Manual)); timeDifference = 0; } return(isTracking); } TrackFrame closestFrame = trackTimeline.ClosestFrom(context.Time); if (closestFrame.Template == null) { // This point has entries in the timeline but doesn't have the corresponding image pattern. // This happen when the timeline is imported from a KVA. currentValue = closestFrame.Location; timeDifference = Math.Abs(context.Time - closestFrame.Time); if (isTracking) { // Make sure we extract the pattern and update the entry at this time. // Note: the position we are using could come from a different time. // But since we are actively tracking we need to end up adding an entry at this time. // But since we don't have any specific template to look for, we will just create an entry at the same location. // If we are on the right time, perfect. If not, it will use the location from the closest and comit it to this time. // When the user switched tracking ON for this drawing, they saw where the point was. // If they moved it manually before changing frame, it will be handled in SetUserValue. // If not, it means they are content with the position it has and thus this insertion is correct. PositionningSource source = timeDifference == 0 ? closestFrame.PositionningSource : PositionningSource.ForcedClosest; trackTimeline.Insert(context.Time, CreateTrackFrame(closestFrame.Location, source)); timeDifference = 0; } return(isTracking); } if (closestFrame.Time == context.Time) { // We found an entry at the exact time requested. currentValue = closestFrame.Location; timeDifference = 0; return(false); } if (!isTracking) { // We did not find the exact requested time in the timeline, and we are not currently tracking. currentValue = closestFrame.Location; timeDifference = Math.Abs(context.Time - closestFrame.Time); return(false); } // We did not find the exact requested time in the timeline, but tracking is active so let's look for the pattern. TrackResult result = Tracker.Track(trackerParameters.SearchWindow, closestFrame, context.Image); if (result.Similarity >= trackerParameters.SimilarityThreshold) { currentValue = result.Location; timeDifference = 0; if (result.Similarity > trackerParameters.TemplateUpdateThreshold) { Bitmap template = BitmapHelper.Copy(closestFrame.Template); TrackFrame newFrame = new TrackFrame(context.Time, result.Location, template, PositionningSource.TemplateMatching); trackTimeline.Insert(context.Time, newFrame); } else { trackTimeline.Insert(context.Time, CreateTrackFrame(result.Location, PositionningSource.TemplateMatching)); } inserted = true; } else { // Tracking failure. currentValue = closestFrame.Location; timeDifference = Math.Abs(context.Time - closestFrame.Time); inserted = false; } return(inserted); }