private void ParseTimeline(XmlReader r, PointF scale, TimestampMapper timeMapper) { trackTimeline.Clear(); bool isEmpty = r.IsEmptyElement; r.ReadStartElement(); while (r.NodeType == XmlNodeType.Element) { switch (r.Name) { case "Frame": TrackFrame frame = new TrackFrame(r, scale, timeMapper); trackTimeline.Insert(frame.Time, frame); break; default: string unparsed = r.ReadOuterXml(); break; } } if (!isEmpty) { r.ReadEndElement(); } }
/// <summary> /// Returns the position of the point nearest to that time. /// This is used by linear kinematics. /// </summary> public PointF GetLocation(long time) { if (trackTimeline.Count == 0) { return(currentValue); } TrackFrame closestFrame = trackTimeline.ClosestFrom(time); return(closestFrame.Location); }
/// <summary> /// Track the point in the current image, or use the existing data if already known. /// </summary> /// <param name="context"></param> public void Track(TrackingContext context) { this.context = context; if (!isTracking) { return; } TrackFrame closestFrame = trackTimeline.ClosestFrom(context.Time); if (closestFrame == null) { currentValue = nonTrackingValue; trackTimeline.Insert(context.Time, CreateTrackFrame(currentValue, PositionningSource.Manual)); return; } if (closestFrame.Template == null) { // We may not have the template if the timeline was imported from KVA. trackTimeline.Insert(context.Time, CreateTrackFrame(closestFrame.Location, closestFrame.PositionningSource)); currentValue = closestFrame.Location; return; } if (closestFrame.Time == context.Time) { currentValue = closestFrame.Location; return; } TrackResult result = Tracker.Track(trackerParameters.SearchWindow, closestFrame, context.Image); if (result.Similarity >= trackerParameters.SimilarityThreshold) { currentValue = result.Location; if (result.Similarity > trackerParameters.TemplateUpdateThreshold) { Bitmap template = closestFrame.Template.CloneDeep(); 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)); } } else { currentValue = closestFrame.Location; } }
/// <summary> /// Tracks a reference template in the given image. Returns similarity score and position of best candidate. /// </summary> public static TrackResult Track(Size searchWindow, TrackFrame reference, Bitmap image) { if (image == null || reference.Template == null) { throw new ArgumentException("image"); } Rectangle searchZone = reference.Location.Box(searchWindow).ToRectangle(); Rectangle imageBounds = new Rectangle(0, 0, image.Width, image.Height); searchZone.Intersect(imageBounds); if (searchZone == Rectangle.Empty) { return(new TrackResult(0, Point.Empty)); } Bitmap template = reference.Template; Rectangle templateBounds = new Rectangle(0, 0, template.Width, template.Height); if (searchZone.Width < template.Width || searchZone.Height < template.Height) { return(new TrackResult(0, Point.Empty)); } BitmapData imageData = image.LockBits(imageBounds, ImageLockMode.ReadOnly, image.PixelFormat); BitmapData templateData = template.LockBits(templateBounds, ImageLockMode.ReadOnly, template.PixelFormat); Image <Bgra, Byte> cvImage = new Image <Bgra, Byte>(imageData.Width, imageData.Height, imageData.Stride, imageData.Scan0); Image <Bgra, Byte> cvTemplate = new Image <Bgra, Byte>(templateData.Width, templateData.Height, templateData.Stride, templateData.Scan0); cvImage.ROI = searchZone; int similarityMapWidth = searchZone.Width - template.Width + 1; int similarityMapHeight = searchZone.Height - template.Height + 1; Image <Gray, Single> similarityMap = new Image <Gray, Single>(similarityMapWidth, similarityMapHeight); CvInvoke.cvMatchTemplate(cvImage.Ptr, cvTemplate.Ptr, similarityMap.Ptr, TM_TYPE.CV_TM_CCOEFF_NORMED); image.UnlockBits(imageData); template.UnlockBits(templateData); Point minLoc = new Point(0, 0); Point maxLoc = new Point(0, 0); double minSimilarity = 0; double maxSimilarity = 0; CvInvoke.cvMinMaxLoc(similarityMap.Ptr, ref minSimilarity, ref maxSimilarity, ref minLoc, ref maxLoc, IntPtr.Zero); Point location = new Point(searchZone.Left + maxLoc.X + template.Width / 2, searchZone.Top + maxLoc.Y + template.Height / 2); return(new TrackResult(maxSimilarity, location)); }
public void ForceInsertClosestLocation() { // This function is used when a drawing containing multiple trackable points has some of the points failing the template matching and others succeeding. // We must always keep the same number of entries in the timelines of all trackable points of a given drawing. // In this function we force the points that failed tracking to insert a dummy value in their timeline. if (!isTracking) { return; } TrackFrame closestFrame = trackTimeline.ClosestFrom(context.Time); if (closestFrame == null) { return; } currentValue = closestFrame.Location; trackTimeline.Insert(context.Time, CreateTrackFrame(currentValue, PositionningSource.ForcedClosest)); }
/// <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); }