private double ReadKnotsFromNeedleLine(LineSegment2D line) { var polarAngleOfNeedle = Math2.ClampAngle(Math2.GetPolarHeadingFromLine(line) - 4); // Skew angle. // 0-360 deg -> 0-180 kt var knots = (polarAngleOfNeedle / 2); if (knots > 175) { knots = 0; } if (knots < 0) { knots = 0; } return(knots); }
public double ReadValue(IndicatorData data, DebugState debugState) { if (TryFindCircleInFullFrame(data, out var circ)) { var focusRect = Math2.CropCircle(circ, 15); var focus = data.Frame.SafeCopy(focusRect); var focusHsv = focus.Convert <Hsv, byte>(); var focusHsvText = focusHsv.DynLowInRange(dyn_lower, new Hsv(180, 255, 255)); var focusHsvTriangleMask = focusHsv.InRange(new Hsv(0, 0, 0), new Hsv(180, 140, 255)); var focusHsvTextOnly = focusHsvText.Copy(focusHsvTriangleMask); debugState.Add(focus); var blobs = Utils.DetectAndFilterBlobs(focusHsvTextOnly, 25, 250). Where(b => b.Centroid.Y >= 5).OrderByDescending(b => b.Area).Take(4); var focusHsvOnlyBlobs = Utils.RemoveAllButBlobs(focusHsvTextOnly, blobs); debugState.Add(focusHsvOnlyBlobs); var parts = GetPacksFromImage(focusHsvOnlyBlobs, blobs, debugState); var ret = ComputeHeadingFromPacks(data.Id, parts, focus, debugState); if (!double.IsNaN(ret)) { // Adjust now that N and S are near-vertical and the blobs are more regular. ret = RefineAngle(ret, focusHsvOnlyBlobs); // Adjust based on the dots on the glareshield. ret += GetGlareShieldSkewAngle(focusRect, data, debugState); // Add a fixed cab skew to account for the vertical perspective. ret -= 1; ret = Math2.ClampAngle(ret); // Proof for the debug info. debugState.Add(focus.Rotate(ret, new Bgr(0, 0, 0))); return(CheckResultValidity(ret, data.Id)); } } else { debugState.SetError("Couldn't find initial circle."); } return(double.NaN); }
double ComputeHeadingFromPacks(int frameId, IEnumerable <BlobPack> packs, Image <Bgr, Byte> compass_frame, DebugState debugState) { var frame = Timeline.Data[frameId]; var my_extended = new ExtendedData(); frame.Heading.ForIndicatorUse = my_extended; var choices = new List <Tuple <double, double, string, Image <Gray, byte> > >(); double unused_angle = 0; var p = packs.OrderByDescending(px => px.BlobRationAngle); foreach (var pack in packs) { var b = pack.BlobBox; b.Inflate(2, 2); var small_angle = pack.BlobRationAngle; var str = ResolveTextFromPreviousFrame(frameId, small_angle); if (string.IsNullOrWhiteSpace(str)) { str = GetText(pack.BlobImage, debugState); } if (str == "N" || str == "E" || str == "S" || str == "W") { small_angle = 360 - Math.Abs(small_angle); double new_heading = 0; switch (str) { case "N": my_extended.LastN = pack.BlobRationAngle; new_heading = small_angle; CvInvoke.Rectangle(compass_frame, b, new Bgr(Color.Blue).MCvScalar, 1); break; case "E": my_extended.LastE = pack.BlobRationAngle; new_heading = (small_angle + 90); CvInvoke.Rectangle(compass_frame, b, new Bgr(Color.Yellow).MCvScalar, 1); break; case "S": my_extended.LastS = pack.BlobRationAngle; new_heading = (small_angle + 180); CvInvoke.Rectangle(compass_frame, b, new Bgr(Color.Red).MCvScalar, 1); break; case "W": my_extended.LastW = pack.BlobRationAngle; new_heading = (small_angle + 270); CvInvoke.Rectangle(compass_frame, b, new Bgr(Color.Lime).MCvScalar, 1); break; } new_heading = Math2.ClampAngle(new_heading); choices.Add(new Tuple <double, double, string, Image <Gray, byte> >(new_heading, small_angle, str, pack.BlobImage)); } else { unused_angle = pack.BlobRationAngle; } } // Fill in exactly one missing quadrant. if (choices.Count == 3 && packs.Count() == 4) { var letters = new List <string>() { "N", "E", "S", "W" }; foreach (var c in choices) { letters.Remove(c.Item3); } var o_angle = unused_angle; unused_angle = 360 - Math.Abs(unused_angle); double new_heading = 0; var str = letters.First(); switch (str) { case "N": my_extended.LastN = o_angle; new_heading = unused_angle; break; case "E": my_extended.LastE = o_angle; new_heading = (unused_angle + 90); break; case "S": my_extended.LastS = o_angle; new_heading = (unused_angle + 180); break; case "W": my_extended.LastW = o_angle; new_heading = (unused_angle + 270); break; } new_heading = Math2.ClampAngle(new_heading); choices.Add(new Tuple <double, double, string, Image <Gray, byte> >(new_heading, (int)unused_angle, str, null)); } if (choices.Count == 4) { // Exclude invalid combinations if (choices.Where(ct => ct.Item3 == "N").Count() > 1) { debugState.SetError("Bad N"); return(double.NaN); } if (choices.Where(ct => ct.Item3 == "E").Count() > 1) { debugState.SetError("Bad E"); return(double.NaN); } if (choices.Where(ct => ct.Item3 == "S").Count() > 1) { debugState.SetError("Bad S"); return(double.NaN); } if (choices.Where(ct => ct.Item3 == "W").Count() > 1) { debugState.SetError("Bad W"); return(double.NaN); } var p1 = Math2.AddAngles(choices[0].Item1, choices[1].Item1); var p2 = Math2.AddAngles(choices[2].Item1, choices[3].Item1); return(Math2.AddAngles(p1, p2)); } else { debugState.SetError($"Bad choices {choices.Count}"); } return(double.NaN); }