private Line2 FitSimilarLine(List <Line2> lines, Line2 target, double angle = 20)
    {
        NumericalRecipes.RansacLine2d rcl = new NumericalRecipes.RansacLine2d();
        List <Vector2> linepoints         = new List <Vector2>();

        linepoints.AddRange(target.SamplePoints());
        //ransac
        for (int i = 0; i < lines.Count; i++)
        {
            if (Line2.IsParallel(lines[i], target, angle))
            {
                if (target.DistanceToLine(lines[i].start) < 30 && target.DistanceToLine(lines[i].end) < 30)
                {
                    linepoints.AddRange(lines[i].SamplePoints());
                }
            }
        }
        Line2 bestline = rcl.Estimate(linepoints);

        if (bestline == null)
        {
            return(target);
        }
        if (Vector2.Dot(bestline.dir, target.dir) < 0)
        {
            bestline.Flip();
        }
        return(bestline);
    }
    public double MinDisBetweenLine2(Line2 a, Line2 b)
    {
        if (!Line2.IsParallel(a, b, 5))
        {
            double dis1 = a.DistanceToLine(b.start);
            double dis2 = a.DistanceToLine(b.end);
            double dis3 = b.DistanceToLine(a.start);
            double dis4 = b.DistanceToLine(a.end);

            double meandis = Math.Min(Math.Min(dis1, dis2), Math.Min(dis3, dis4));
            //double meandis = (dis1 + dis2 + dis3 + dis4) / 4.0;
            return(meandis);
        }
        else
        {
            return(0);
        }
    }
    private Line2 FitCloseLine(List <Line2> lines, Line2 target, double dis = 30)
    {
        List <Vector2> linepoints = new List <Vector2>();

        linepoints.AddRange(target.SamplePoints());
        //ransac
        for (int i = 0; i < lines.Count; i++)
        {
            if (Line2.IsParallel(lines[i], target, 45))
            {
                double d = double.MaxValue;
                foreach (Vector2 v in linepoints)
                {
                    d = Math.Min(Math.Min(Vector2.Distance(lines[i].start, v), Vector2.Distance(lines[i].end, v)), d);
                }
                if (d < dis)
                {
                    linepoints.AddRange(lines[i].SamplePoints());
                }
            }
        }

        // least square
        //Vector2 dir = Utility.GetDirection(linepoints);
        //Line2 bestline = new Line2(linepoints[0], dir, true);
        //foreach (Vector2 v in linepoints)
        //    bestline.UpdateEnd(v);

        //ransac
        NumericalRecipes.RansacLine2d rcl = new NumericalRecipes.RansacLine2d();
        Line2 bestline = rcl.Estimate(linepoints);

        if (bestline == null)
        {
            return(target);
        }

        if (Vector2.Dot(bestline.dir, target.dir) < 0)
        {
            bestline.Flip();
        }
        return(bestline);
    }
    private List <Vector2> ApproximateStraightAxis(List <Line2> skel_lines)
    {
        // from skeleton lines(aka. thining image lines) (find lines that have the same direction with guessed axis <- when top face is known)
        // ransac, fit one line as main axis
        // update end points
        // set top circle center as start point
        // extend end point

        Line2             new_main_axis = null;
        Image <Rgb, byte> mainaxis_img  = body_img.Copy().Convert <Rgb, byte>();

        Line2 main_axis = null;

        if (iscube)
        {
            main_axis = ApproximateFromCubeTop();
        }
        else
        {
            main_axis = ApproximateFromTop();
        }

        if (main_axis == null)
        {
            return(null);
        }

        if (noface)
        {
            NumericalRecipes.RansacLine2d rcl = new NumericalRecipes.RansacLine2d();
            List <Vector2> thin_points        = IExtension.GetMaskPoints(this.ori_thin_img);
            List <Vector2> linepoints         = new List <Vector2>();
            for (int i = 0; i < skel_lines.Count; i++)
            {
                linepoints.AddRange(skel_lines[i].SamplePoints());
            }
            //Line2 bestline = rcl.Estimate(linepoints);
            Line2  bestline = rcl.Estimate(thin_points);
            double diss     = Vector2.Distance(bestline.start, face_center[0]);
            double dise     = Vector2.Distance(bestline.end, face_center[0]);
            if (dise < diss)
            {
                bestline.Flip();
            }
            new_main_axis = bestline;
        }
        else
        {
            // use ori thinning image as guidance
            LineSegment2D[] lines = ori_thin_img.HoughLinesBinary(
                1,               //Distance resolution in pixel-related units
                Math.PI / 180.0, //Angle resolution measured in radians.
                3,               //threshold
                4,               //min Line width
                1                //gap between lines
                )[0];            //Get the lines from the first channel

            skel_lines.Clear();
            foreach (LineSegment2D line in lines)
            {
                skel_lines.Add(new Line2(new Vector2(line.P1.X, line.P1.Y), new Vector2(line.P2.X, line.P2.Y)));
            }

            Line2 bestline = FitSimilarLine(skel_lines, main_axis);
            if (Line2.IsParallel(bestline, main_axis, 5))
            {
                new_main_axis = bestline;
            }
            else
            {
                new_main_axis = main_axis;
            }
        }

        // update end
        List <Vector2> ori_skel_points = IExtension.GetMaskPoints(ori_thin_img);

        for (int i = 0; i < ori_skel_points.Count; i++)
        {
            new_main_axis.UpdateEnd(ori_skel_points[i]);
        }
        new_main_axis.start = new_main_axis.ProjToLine(face_center.First());
        new_main_axis.start = new_main_axis.GetPointwithT(4);

        List <Vector2> axis_points = new List <Vector2>();

        // axis_points.Add(face_center.First());
        axis_points.AddRange(new_main_axis.SamplePoints());
        axis_points.Add(new_main_axis.GetPointwithT((float)new_main_axis.Length() * 1.2f));
        axis_points = IExtension.ResetPath(axis_points, 3);

        #region visualize
        Image <Rgb, byte> mainaxis_point_img = body_img.Copy().Convert <Rgb, byte>();
        foreach (Vector2 v in axis_points)
        {
            mainaxis_point_img.Draw(new CircleF(new PointF(v.x, v.y), 2.0f), new Rgb(255, 0, 0), 1);
        }
        mainaxis_point_img.Draw(new CircleF(new PointF(axis_points.First().x, axis_points.First().y), 3.0f), new Rgb(0, 255, 0), 1);
        mainaxis_point_img.Save(index_forname.ToString() + this.label_forname.ToString() + "_axis_straight.png");
        #endregion

        return(axis_points);
    }