Пример #1
0
        private double CalculateResultFromAllResults(List <double> ret, DebugState debugState)
        {
            if (ret.Count == 0)
            {
                debugState.SetError("zero result count");
                return(double.NaN);
            }

            ret.Sort();

            var finalKnots = ret.Average();

            if (ret.Count > 3)
            {
                finalKnots = ret[ret.Count / 2];
            }

            if (!IsValueInExpectedRange(finalKnots) && num_rejected_values < 10)
            {
                num_rejected_values++;
                debugState.SetError($"REJECT: kt={finalKnots} lastValue={Timeline.Speed}");
                return(double.NaN);
            }

            num_rejected_values = 0;
            return(finalKnots);
        }
Пример #2
0
        public double ReadValue(IndicatorData data, DebugState debugState)
        {
            if (TryFindCircleInFullFrame(data, out var circle))
            {
                var focus = data.Frame.Copy(Math2.CropCircle(circle, 10));
                debugState.Add(focus);

                List <double> ret = new List <double>();
                foreach (var line in GetLinesFromFocusImage(focus, circle, debugState))
                {
                    CvInvoke.Line(focus, line.P1, line.P2, new Bgr(Color.Yellow).MCvScalar, 2);

                    var knots = ReadKnotsFromNeedleLine(line);

                    if (IsValueInExpectedRange(knots))
                    {
                        ret.Add(knots);
                    }
                }

                return(CalculateResultFromAllResults(ret, debugState));
            }
            else
            {
                debugState.SetError("No circles");
            }
            return(double.NaN);
        }
Пример #3
0
        public double ReadValue(IndicatorData data, DebugState debugState)
        {
            if (TryFindRollCircleInFullFrame(data, out CircleF rollIndicatorCicle))
            {
                var FocusRect = Math2.CropCircle(rollIndicatorCicle, 10);
                var focus     = data.Frame.SafeCopy(FocusRect);

                debugState.Add(focus);

                // Isolate the outside ring
                Mat maskInnerAlt = new Mat(focus.Size, DepthType.Cv8U, 3);
                maskInnerAlt.SetTo(new MCvScalar(1));
                CvInvoke.Circle(maskInnerAlt, new Point(focus.Size.Width / 2, focus.Size.Height / 2), (int)(rollIndicatorCicle.Radius - (rollIndicatorCicle.Radius * 0.2)), new Bgr(Color.White).MCvScalar, -1);
                CvInvoke.Circle(maskInnerAlt, new Point(focus.Size.Width / 2, focus.Size.Height / 2), (int)(rollIndicatorCicle.Radius - (rollIndicatorCicle.Radius * 0.275)), new Bgr(Color.Black).MCvScalar, -1);

                var outerMovingRingOnly          = focus.Copy(maskInnerAlt.ToImage <Gray, byte>());
                var outerMovingRingWithoutBottom = outerMovingRingOnly.Copy(new Rectangle(0, 0, outerMovingRingOnly.Width, (int)(outerMovingRingOnly.Height))); // - (outerMovingRingOnly.Height * 0.29)
                var ring_hsv_unfiltered          = outerMovingRingWithoutBottom.Convert <Hsv, byte>().InRange(new Hsv(20, 0, 85), new Hsv(180, 255, 255));
                var ring_hsv = Utils.RemoveBlobs(ring_hsv_unfiltered, 1, 500);

                debugState.Add(outerMovingRingOnly);
                debugState.Add(outerMovingRingWithoutBottom);
                debugState.Add(ring_hsv);
                debugState.Add(ring_hsv_unfiltered);

                return(ReadRollAngleFromRingImage(ring_hsv, focus, debugState));
            }
            else
            {
                debugState.SetError($"ROLL: Main circles");
            }
            return(double.NaN);
        }
Пример #4
0
        public double ReadValue(IndicatorData data, DebugState debugState)
        {
            if (TryFindCircleInFullFrame(data, out var circle))
            {
                var focus       = data.Frame.SafeCopy(Math2.CropCircle(circle, 15));
                var vs_blackimg = focus.Convert <Hsv, byte>().DynLowInRange(dyn_lower, new Hsv(180, 255, 255));

                debugState.Add(focus);
                debugState.Add(vs_blackimg);

                int margin     = 10;
                var vspeedMask = new Mat(focus.Size, DepthType.Cv8U, 3);
                vspeedMask.SetTo(new MCvScalar(1));
                CvInvoke.Circle(vspeedMask, Point.Round(new PointF(circle.Radius + margin, circle.Radius + margin)), (int)(circle.Radius - (circle.Radius * 0.1)), new Bgr(Color.White).MCvScalar, -1);
                vs_blackimg = vs_blackimg.Copy(vspeedMask.ToImage <Gray, byte>());

                var vspeed_inner_only = vs_blackimg.Copy(new Rectangle(0, 0, vs_blackimg.Width / 2, vs_blackimg.Height));

                debugState.Add(vspeed_inner_only);

                Rectangle center = GetCenterBoxFromImage(focus);
                foreach (var line in CvInvoke.HoughLinesP(vspeed_inner_only, 1, Math.PI / 45.0, 20, 20, 14))
                {
                    if (center.Contains(line.P1) || center.Contains(line.P2))
                    {
                        CvInvoke.Line(focus, line.P1, line.P2, new Bgr(Color.Yellow).MCvScalar, 2);

                        LineSegment2D needleLine;
                        if (center.Contains(line.P1))
                        {
                            needleLine = new LineSegment2D(line.P2, line.P1);
                        }
                        else
                        {
                            needleLine = new LineSegment2D(line.P1, line.P2);
                        }

                        var angle = (Math2.GetPolarHeadingFromLine(needleLine) - 270);
                        // bias up to account for skew
                        angle += 2.75;

                        if (line.Length > 63)
                        {
                            debugState.SetError($"Rejected length: {line.Length}");
                        }
                        return(angle);
                    }
                }
            }
            return(double.NaN);
        }
Пример #5
0
        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);
        }
Пример #6
0
        private double ReadRollAngleFromRingImage(Image <Gray, byte> img, Image <Bgr, byte> markupImage, DebugState debugState)
        {
            var circles = CvInvoke.HoughCircles(img, HoughType.Gradient, 2.0, 1.0, 1, 10, 50, 53);

            if (circles.Length > 0)
            {
                var cir    = circles[0];
                var radius = cir.Radius;

                Func <int, int, bool> check = (int cX, int cY) =>
                {
                    if (cX < img.Width && cX > 0 &&
                        cY < img.Height && cY > 0)
                    {
                        var b = img[cY, cX];
                        return(b.Intensity > 0);
                    }
                    return(false);
                };

                Func <int, int, bool> check2 = (int cX, int cY) =>
                {
                    return(check(cX - 1, cY) ||
                           check(cX + 1, cY) ||
                           check(cX - 3, cY) ||
                           check(cX + 3, cY) ||
                           check(cX - 5, cY) ||
                           check(cX + 5, cY) ||
                           check(cX, cY + 2) ||
                           check(cX, cY - 2));
                };

                // From 6PM to 12PM clockwise
                PointF leftPoint = default(PointF);
                for (double t = Math.PI / 2; t < Math.PI * 1.5; t += 0.05f)
                {
                    var cX_ = (int)(radius * Math.Cos(t) + cir.Center.X);
                    var cY_ = (int)(radius * Math.Sin(t) + cir.Center.Y);

                    //CvInvoke.Circle(focus, new Point(cX_, cY_), 1, new Bgr(Color.Red).MCvScalar, 1);

                    if (check2(cX_, cY_))
                    {
                        leftPoint = new PointF(cX_, cY_);
                        break;
                    }
                }

                // From 6PM to 12PM counter-clockwise.
                PointF rightPoint = default(PointF);
                for (double t = Math.PI / 2; t > -0.5 * Math.PI; t -= 0.05f)
                {
                    var cX_ = (int)(radius * Math.Cos(t) + cir.Center.X);
                    var cY_ = (int)(radius * Math.Sin(t) + cir.Center.Y);

                    //CvInvoke.Circle(focus, new Point(cX_, cY_), 1, new Bgr(Color.Green).MCvScalar, 1);

                    if (check2(cX_, cY_))
                    {
                        rightPoint = new PointF(cX_, cY_);
                        break;
                    }
                }

                if (leftPoint != default(PointF) && rightPoint != default(PointF))
                {
                    CvInvoke.Line(markupImage, rightPoint.ToPoint(), leftPoint.ToPoint(), new Bgr(Color.Yellow).MCvScalar, 2);

                    var horizontalNeedleLine = new LineSegment2DF(leftPoint, rightPoint);

                    if (horizontalNeedleLine.Length < 88 || horizontalNeedleLine.Length > 140)
                    {
                        debugState.SetError($"ROLL: Dist {horizontalNeedleLine.Length}");
                        return(double.NaN);
                    }

                    var angle = Math2.GetPolarHeadingFromLine(horizontalNeedleLine) - 270;
                    // skew considered from other panels
                    return(angle + 1);
                }
                else
                {
                    debugState.SetError($"ROLL: Couldn't find boundary");
                }
            }
            else
            {
                debugState.SetError($"ROLL: boundary circles");
            }
            return(double.NaN);
        }
Пример #7
0
        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);
        }
Пример #8
0
        private double GetGlareShieldSkewAngle(Rectangle focusRect, IndicatorData data, DebugState debugState)
        {
            var topDotRect    = new Rectangle(focusRect.Left - 160, focusRect.Top - 5, 40, 40);
            var bottomDotRect = new Rectangle(focusRect.Left - 170, focusRect.Bottom - 40, 60, 40);

            var topPointBlobs    = GetDotLocationFromFullFrame(data.Frame, focusRect, topDotRect, isTop: true);
            var bottomPointBlobs = GetDotLocationFromFullFrame(data.Frame, focusRect, bottomDotRect, isTop: false);

            double a = double.NaN;

            if (topPointBlobs.Count > 0 && bottomPointBlobs.Count > 0)
            {
                var topPoint    = topPointBlobs.First().Centroid.Add(topDotRect.Location);
                var bottomPoint = bottomPointBlobs.First().Centroid.Add(bottomDotRect.Location);

                a = Math2.GetPolarHeadingFromLine(topPoint, bottomPoint);
                if (a > 180)
                {
                    a  = 360 - a;
                    a *= -1;
                }

                var lineRect = new Rectangle(focusRect.Left - 200, focusRect.Top - 50, 170, focusRect.Bottom + 50 - focusRect.Top);
                var lineImg  = data.Frame.Copy(lineRect);

                var topInLineImg    = new PointF(topPoint.X - lineRect.Location.X, topPoint.Y - lineRect.Location.Y);
                var bottomInLineImg = new PointF(bottomPoint.X - lineRect.Location.X, bottomPoint.Y - lineRect.Location.Y);

                var topRange = lineImg.Convert <Hsv, byte>(); //.InRange(new Hsv(HLow, SLow, VLow), new Hsv(HHigh, SHigh, VHigh));
                CvInvoke.Line(lineImg, topInLineImg.ToPoint(), bottomInLineImg.ToPoint(), new Bgr(Color.Yellow).MCvScalar, 1);
                debugState.Add(lineImg);
                debugState.Add(topRange);

                var dist = Math2.GetDistance(topPoint, bottomPoint);
                if (Math.Abs(a) > 8 || dist < 110 || dist > 120)
                {
                    var biasFrame = Timeline.LatestFrame(f => f.Heading.ForIndicatorUse == null ? double.NaN : ((ExtendedData)f.Heading.ForIndicatorUse).Bias, data.Id);
                    if (biasFrame != null)
                    {
                        a = ((ExtendedData)biasFrame.Heading.ForIndicatorUse).Bias;
                    }
                    else
                    {
                        debugState.SetError("Rejected due to dots angle out of bounds " + a);
                        return(double.NaN);
                    }
                }
            }
            else
            {
                var biasFrame = Timeline.LatestFrame(f => f.Heading.ForIndicatorUse == null ? double.NaN : ((ExtendedData)f.Heading.ForIndicatorUse).Bias, data.Id);
                if (biasFrame != null)
                {
                    a = ((ExtendedData)biasFrame.Heading.ForIndicatorUse).Bias;
                }
                else
                {
                    debugState.SetError("Rejected due to dots angle out of bounds " + a);
                    return(double.NaN);
                }
            }

            ((ExtendedData)Timeline.Data[data.Id].Heading.ForIndicatorUse).Bias = a;
            return(a / 2);
        }
Пример #9
0
        public double ReadValue(IndicatorData data, DebugState debugState)
        {
            if (TryFindCircleInFullFrame(data, out var circle))
            {
                var focus          = data.Frame.SafeCopy(Math2.CropCircle(circle, 15));
                var vs_blackimg    = focus.Convert <Hsv, byte>().DynLowInRange(dyn_lower, new Hsv(180, 255, 255)).PyrUp().PyrDown();
                var markedup_frame = vs_blackimg.Convert <Bgr, byte>();
                debugState.Add(focus);
                debugState.Add(vs_blackimg);
                debugState.Add(markedup_frame);

                var cannyEdges3 = new Mat();
                CvInvoke.Canny(vs_blackimg, cannyEdges3, 10, 120);
                Mat dialatedCanny = new Mat();
                CvInvoke.Dilate(cannyEdges3, dialatedCanny, null, new Point(-1, -1), 1, BorderType.Default, new Gray(0).MCvScalar);

                Rectangle center = GetCenterBoxFromImage(focus);
                CvInvoke.Rectangle(markedup_frame, center, new Bgr(Color.Red).MCvScalar, 1);

                var lines = CvInvoke.HoughLinesP(dialatedCanny, 1, Math.PI / 45.0, 20, 16, 0).OrderByDescending(l => l.Length);
                foreach (var line in lines)
                {
                    CvInvoke.Line(markedup_frame, line.P1, line.P2, new Bgr(Color.Red).MCvScalar, 1);

                    if (center.Contains(line.P1) || center.Contains(line.P2))
                    {
                        LineSegment2D needleLine;
                        if (center.Contains(line.P1))
                        {
                            needleLine = new LineSegment2D(line.P2, line.P1);
                        }
                        else
                        {
                            needleLine = new LineSegment2D(line.P1, line.P2);
                        }

                        var angle    = Math2.GetPolarHeadingFromLine(needleLine);
                        var hundreds = Math.Round((angle / 360) * 999);

                        CvInvoke.Line(focus, needleLine.P1, needleLine.P2, new Bgr(Color.Yellow).MCvScalar, 2);

                        var candidates = new List <double>();
                        for (var i = 0; i < 9; i++)
                        {
                            candidates.Add(hundreds + (i * 1000));
                        }

                        var ret = candidates.OrderBy(c => Math.Abs(c - Timeline.Altitude)).First();

                        if (!double.IsNaN(Timeline.Altitude))
                        {
                            var delta = Math.Abs(ret - Timeline.Altitude);
                            if (delta > 400)
                            {
                                debugState.SetError($"Bad value {delta} > 400");
                                return(double.NaN);
                            }
                        }
                        else
                        {
                            return(0);
                        }

                        return(ret);
                    }
                }
            }
            return(double.NaN);
        }