private List <Vector2> ApproximateCurveAxis(List <Line2> skel_lines) { Image <Rgb, byte> curveaxis_img = body_img.Copy().Convert <Rgb, byte>(); List <Vector2> skel_points = new List <Vector2>(); //Image<Gray, byte> temp = face_img[0].Copy(); //var element = CvInvoke.GetStructuringElement(ElementShape.Cross, new Size(3, 3), new Point(-1, -1)); //CvInvoke.Dilate(temp, temp, element, new Point(-1, -1), 10, BorderType.Reflect, default(MCvScalar)); //CvInvoke.Subtract(ori_prune_img, temp, ori_prune_img); skel_points = IExtension.GetMaskPoints(ori_prune_img); for (int i = 0; i < skel_points.Count; i++) { if (ori_prune_img[(int)skel_points[i].y, (int)skel_points[i].x].Equals(new Gray(128))) { this.end_points.Add(i); } } List <Vector2> path_points = new List <Vector2>(); List <Vector2> top_mask_point = IExtension.GetMaskPoints(this.face_img[0]); Vector2 top_center = Utility.Average(top_mask_point); //double guess_radius = Math.Sqrt(top_mask_point.Count / Math.PI); Vector2 start_point = top_center; List <Vector2> attach_boundary = new List <Vector2>(); if (noface) { // Vector2 attach_center = Utility.Average(IExtension.GetBoundary(this.attach_img)); // start_point = attach_center; attach_boundary = IExtension.GetBoundary(this.attach_img); } // closest to top face center int startidx = 0; double mindis_topcenter = double.MaxValue; for (int i = 0; i < end_points.Count; i++) { double dis = double.MaxValue; if (noface) { dis = Utility.DistancePoint2Set(skel_points[end_points[i]], attach_boundary); } else { dis = Vector2.Distance(skel_points[end_points[i]], start_point); } if (dis < mindis_topcenter) { mindis_topcenter = dis; startidx = end_points[i]; } } path_points = FindLongestPath(skel_points, startidx); double diss = Vector2.Distance(path_points.First(), start_point); double dise = Vector2.Distance(path_points.Last(), start_point); if (dise < diss) { path_points.Reverse(); } path_points = IExtension.ResetPath(path_points, 20); // add first point if (!noface) { path_points.Insert(0, top_center); // extend ending Line2 endray = new Line2(path_points[path_points.Count - 2], path_points[path_points.Count - 1]); path_points.Add(endray.GetPointwithT((float)endray.Length() * 10)); //reset path to 3 path_points = IExtension.ResetPath(path_points, 3); } else { path_points = IExtension.ResetPath(path_points, 3); path_points.RemoveRange(0, (int)(0.05 * path_points.Count)); //path_points.Insert(0, top_center); } #region visualize curveaxis_img = body_img.Copy().Convert <Rgb, byte>(); for (int i = 0; i < path_points.Count; i++) { curveaxis_img.Draw(new CircleF(new PointF(path_points[i].x, path_points[i].y), 2.0f), new Rgb(255, 0, i * 2), 1); } curveaxis_img.Draw(new CircleF(new PointF(path_points[0].x, path_points[0].y), 2.0f), new Rgb(0, 255, 0), 1); curveaxis_img.Save(index_forname.ToString() + this.label_forname.ToString() + "_axis_curve.png"); #endregion return(path_points); }
private Line2 ApproximateFromTop() { //guess axis with top face(s) Line2 main_axis = null; face_center = new List <Vector2>(); List <Vector2> normal = new List <Vector2>(); List <double> minoraxislength = new List <double>(); foreach (Image <Gray, byte> fimg in face_img) { Vector2 mean = new Vector2(); List <Vector2> points = IExtension.GetMaskPoints(fimg); foreach (Vector2 p in points) { mean += p; } mean /= points.Count; face_center.Add(mean); float W = points.Count; float[,] C = new float[2, 2]; foreach (Vector2 point in points) { C[0, 0] += (point.x - mean.x) * (point.x - mean.x); C[0, 1] += (point.x - mean.x) * (point.y - mean.y); C[1, 0] += (point.y - mean.y) * (point.x - mean.x); C[1, 1] += (point.y - mean.y) * (point.y - mean.y); } C[0, 0] /= W; C[0, 1] /= W; C[1, 0] /= W; C[1, 1] /= W; Matrix2d CM = new Matrix2d(C); NumericalRecipes.SVD svd = new NumericalRecipes.SVD(C); //svd.w - eigenvalue, start from 1 //svd.u - eigenvector, start from 1 int max = 1, min = 2; if (svd.w[max] < svd.w[min]) { int temp = max; max = min; min = temp; } float major = 2 * Mathf.Sqrt(svd.w[max]); Vector2 majoraxis = new Vector2(svd.u[1, max], svd.u[2, max]); majoraxis.Normalize(); float minor = 2 * Mathf.Sqrt(svd.w[min]); Vector2 minoraxis = new Vector2(svd.u[1, min], svd.u[2, min]); minoraxis.Normalize(); Vector2 majorendp = mean + majoraxis * major; Vector2 majorstartp = mean - majoraxis * major; Vector2 minorendp = mean + minoraxis * minor; Vector2 minorstartp = mean - minoraxis * minor; //minoraxislength.Add(Vector2.Distance(minorendp, minorstartp)); minoraxislength.Add((double)minor * 2); normal.Add(Utility.PerpendicularRight(majoraxis)); //normal.Add(majoraxis); } if (face_center.Count > 1) { //Vector2 mean_normal = normal.First(); Line2 best_top_normal_line = new Line2(face_center.First(), normal.First(), true); double maxdis = 0; int farthesttopidx = 0; for (int i = 1; i < face_center.Count; i++) { double tofirstcenterdis = Vector2.Distance(face_center[0], face_center[i]); if (tofirstcenterdis > maxdis) { maxdis = tofirstcenterdis; farthesttopidx = i; } double dis = best_top_normal_line.DistanceToLine(face_center[i]); if (dis > 10) { main_axis = null; } } main_axis = new Line2(face_center.First(), face_center[farthesttopidx]); } else { Line2 top_normal_line = new Line2(face_center.First(), normal.First(), true); Vector2 online_point = top_normal_line.GetPointwithT((float)minoraxislength[0]); if (online_point.x >= 0 && online_point.y >= 0 && online_point.x < body_img.Width && online_point.y < body_img.Height) { if (!this.body_img[(int)online_point.y, (int)online_point.x].Equals(new Gray(255))) { top_normal_line.Flip(); } } else { top_normal_line.Flip(); } main_axis = top_normal_line; } return(main_axis); }
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); }
private Line2 ApproximateFromCubeTop() { #region get corner points // get corner points VectorOfVectorOfPoint con = new VectorOfVectorOfPoint(); Image <Gray, byte> img_copy = this.face_img[0].Copy(); CvInvoke.FindContours(img_copy, con, img_copy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple); int maxcomp = 0, max_con_idx = -1; for (int i = 0; i < con.Size; i++) { if (con[i].Size > maxcomp) { maxcomp = con[i].Size; max_con_idx = i; } } // found max component's contour // simplify contours VectorOfPoint con2 = new VectorOfPoint(); con2 = con[max_con_idx]; double alpha = 0.01; // 越小越接近曲线 int iter = 0; List <Vector2> corner_points = new List <Vector2>(); while (con2.Size != 4) { if (iter++ > 200) { break; } double epsilon = alpha * CvInvoke.ArcLength(con[max_con_idx], true); CvInvoke.ApproxPolyDP(con[max_con_idx], con2, epsilon, true); if (con2.Size > 4) { alpha += 0.01; corner_points.Clear(); for (int i = 0; i < con2.Size; i++) { corner_points.Add(new Vector2(con2[i].X, con2[i].Y)); } } if (con2.Size < 4) { alpha -= 0.003; } } if (con2.Size == 4) { corner_points.Clear(); for (int i = 0; i < con2.Size; i++) { corner_points.Add(new Vector2(con2[i].X, con2[i].Y)); } } else { int removei = corner_points.Count - 4; for (int i = 0; i < removei; i++) { corner_points.RemoveAt(1); } } face_center = new List <Vector2>(); face_center.Add(Utility.Average(corner_points)); List <Vector2> boundary2_face = IExtension.GetBoundary(this.face_img[0]); List <Vector2> boundary2_body = ExtractOutline(this.body_img, boundary2_face); int[] i_corner = SelectTwoCorners(corner_points, boundary2_body, this.body_img); #endregion // body img fit line Image <Gray, byte> temp = body_img.Copy(); Image <Gray, byte> body_bound = temp.Canny(60, 100); LineSegment2D[] lines = body_bound.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 var element = CvInvoke.GetStructuringElement(ElementShape.Cross, new Size(3, 3), new Point(-1, -1)); CvInvoke.Dilate(this.face_img[0], temp, element, new Point(-1, -1), 30, BorderType.Reflect, default(MCvScalar)); //temp.Save("face_dilate.png"); Image <Gray, byte> lineimg = temp.CopyBlank(); List <Line2> body_lines = new List <Line2>(); foreach (LineSegment2D line in lines) { if (!temp[line.P1.Y, line.P1.X].Equals(new Gray(255)) && !temp[line.P2.Y, line.P2.X].Equals(new Gray(255))) { body_lines.Add(new Line2(new Vector2(line.P1.X, line.P1.Y), new Vector2(line.P2.X, line.P2.Y))); lineimg.Draw(line, new Gray(255), 2); } } //lineimg.Save("body_line.png"); /// use corner points to find lines double mindis0 = double.MaxValue, mindis1 = double.MaxValue; int line0 = -1, line1 = -1; for (int i = 0; i < body_lines.Count; i++) { double dis0 = Mathf.Min(Vector2.Distance(corner_points[i_corner[0]], body_lines[i].start), Vector2.Distance(corner_points[i_corner[0]], body_lines[i].end)); double dis1 = Mathf.Min(Vector2.Distance(corner_points[i_corner[1]], body_lines[i].start), Vector2.Distance(corner_points[i_corner[1]], body_lines[i].end)); if (dis0 < mindis0) { mindis0 = dis0; line0 = i; } if (dis1 < mindis1) { mindis1 = dis1; line1 = i; } } // find similar // straight line Line2 body_line0 = FitCloseLine(body_lines, body_lines[line0], 50); Line2 body_line1 = FitCloseLine(body_lines, body_lines[line1], 50); if (Vector2.Distance(corner_points[i_corner[0]], body_line0.start) < Vector2.Distance(corner_points[i_corner[0]], body_line0.end)) { body_line0.Flip(); } if (Vector2.Distance(corner_points[i_corner[1]], body_line1.start) < Vector2.Distance(corner_points[i_corner[1]], body_line1.end)) { body_line1.Flip(); } Vector2 meandir = (body_line0.dir + body_line1.dir).normalized; Line2 top_normal_line = new Line2(face_center.First(), meandir, true); Vector2 online_point = top_normal_line.GetPointwithT(Vector2.Distance(corner_points[0], corner_points[1])); if (online_point.x >= 0 && online_point.y >= 0 && online_point.x < body_img.Width && online_point.y < body_img.Height) { if (!this.body_img[(int)online_point.y, (int)online_point.x].Equals(new Gray(255))) { top_normal_line.Flip(); } } else { top_normal_line.Flip(); } top_normal_line.UpdateEnd(new Vector2(500, 500)); top_normal_line.UpdateEnd(new Vector2(0, 0)); if (debug) { Image <Rgb, byte> cube_outline = this.body_img.Copy().Convert <Rgb, byte>(); cube_outline.Draw(new CircleF(new PointF(corner_points[i_corner[0]].x, corner_points[i_corner[0]].y), 10.0f), new Rgb(255, 0, 0), 1); cube_outline.Draw(new CircleF(new PointF(corner_points[i_corner[1]].x, corner_points[i_corner[1]].y), 10.0f), new Rgb(255, 0, 0), 1); cube_outline.Draw(new CircleF(new PointF(corner_points[0].x, corner_points[0].y), 8), new Rgb(0, 0, 255), 1); cube_outline.Draw(new CircleF(new PointF(corner_points[1].x, corner_points[1].y), 8), new Rgb(0, 0, 255), 1); cube_outline.Draw(new CircleF(new PointF(corner_points[2].x, corner_points[2].y), 8), new Rgb(0, 0, 255), 1); cube_outline.Draw(new CircleF(new PointF(corner_points[3].x, corner_points[3].y), 8), new Rgb(0, 0, 255), 1); cube_outline.Draw(body_lines[line0].ToLineSegment2D(), new Rgb(0, 255, 0), 3); cube_outline.Draw(body_lines[line1].ToLineSegment2D(), new Rgb(0, 255, 0), 3); cube_outline.Draw(body_line0.ToLineSegment2D(), new Rgb(0, 255, 255), 3); cube_outline.Draw(body_line1.ToLineSegment2D(), new Rgb(0, 0, 255), 3); cube_outline.Save("cube_outline.png"); Image <Gray, byte> approx_top = this.body_img.Copy(); foreach (Vector2 v in top_normal_line.SamplePoints()) { approx_top.Draw(new CircleF(new PointF(v.x, v.y), 2.0f), new Gray(0), 1); } approx_top.Save("approx.png"); } return(top_normal_line); }