【问题标题】:Quadrilateral Shape Finding Algorithm四边形形状查找算法
【发布时间】:2012-12-06 17:20:21
【问题描述】:

我想从随机定位的线段中检测并完成所有可能的四边形!

附上的照片是一个例子,线条可能总是出现在非常不同的位置。

任何人都可以为此指出任何好的算法吗?

  • 注意线段是使用 opencv 2.4.2 进行霍夫变换的输出

解决方案是检测预测黄色四边形

【问题讨论】:

  • 您的示例图片没有完整的四边形。你能提供一个有的吗?
  • 这就是重点,大多数情况下图像不会有完整的四边形!我想检测并完成 iamge 中的所有四边形
  • 你能在你的例子中发布,那么预期的输出是什么?您在寻找正方形、长方形、梯形还是任何四边形?
  • 我做到了,我正在寻找任何四边形
  • 另外,我认为您的意思可能是简单或凸四边形。 en.wikipedia.org/wiki/File:Quadrilateral_hierarchy.png

标签: c# algorithm image-processing opencv artificial-intelligence


【解决方案1】:

解决方案 1:
这是使用 OpenCV 2.4 和 Sympy 用 python 2.7.x 编写的完整解决方案。
我使用了 D.Noakes 的数据(线段),但我采用了不同的方法。

问题定义:
对于一组线段,找出所有可能的四边形形状,其中线段适合四边形的边缘。

方法:

  • 将线段分组为大致“水平”或“垂直”。
  • 成对的“水平”或“垂直”。
  • 过滤器对,例如如果它们接触或相交。
  • 组合两个“水平”段和两个“垂直”段。
  • 过滤候选四边形,例如如果角在图像之外或片段不在四边形上。

结果:
该方法检测图像中的4个四边形

查看动画 GIF:https://ibb.co/4Rv9rJW

代码:https://pastiebin.com/5f3836269f7e5

#!/usr/bin/env python

"""
Find Quads:

For a set of line segments, find all the possible
quadrilateral shapes where the segments fit
inside the edges of the quad.


Dependencies:
Sympy is used for geometry primitives.
sudo pip install sympy
"""

import numpy as np
import cv2
import itertools # combinations, product
from sympy import Point, Line, Segment, convex_hull
import sys


input_image = cv2.imread("detected_lines.jpg")


#------------------------------------------------------------------------------#

def checkPointInImage(point, image_width, image_height):
    """
    Check if a Sympy Point2D is within the bounds of an OpenCV image.
    """
    pt_x = int(round(point.x))
    pt_y = int(round(point.y))
    if (pt_x >= 0) and (pt_x < image_width) and (pt_y >= 0) and (pt_y < image_height):
        return True
    # Point is outside the image boundary
    return False


def checkPointsInImage(points, image_width, image_height):
    """
    Check if a set of Sympy Point2D are all within the bounds of an OpenCV image.
    """
    for point in points:
        if not checkPointInImage(point, image_width, image_height):
            return False
    # All points are within the image boundary
    return True


def getUniquePairs(segments, image_dims):
    """
    Get all the possible pairs of line segments.
    (the unique combinations of 2 lines)
    Note: this doesn't check for duplicate elements, it works
    only on the position in the list.
    """

    # Check that a pair of segments are not intersecting
    check_segments_dont_intersect = True

    # Check that the endpoint of one segment
    # does not touch the other segment (within 10 pixels)
    check_segment_endpoints = True
    endpoint_min_separation = 10

    # Project the segments and check if the intersection
    # point is within the image
    check_projected_segments_dont_intersect = True

    pairs = list(itertools.combinations(segments, 2)) # a list of tuple

    image_width, image_height = image_dims

    filtered_pairs = []
    for pair in pairs:
        segment1 = pair[0]
        segment2 = pair[1]

        if check_segments_dont_intersect:
            if bool(len(segment1.intersection(segment2))):
                # Discard this pair.
                # The pair of segments intersect each other.
                continue

        if check_segment_endpoints or check_projected_segments_dont_intersect:
            line1 = Line(segment1)
            line2 = Line(segment2)
            intersection_points = line1.intersection(line2)
            intersects = bool(len(intersection_points))

            if intersects:
                intersection_point = intersection_points[0]

                if check_segment_endpoints:
            # Measure the distance from the endpoint of each segment
                    # to the intersection point.
                    d1 = float(segment1.points[0].distance(intersection_point))
                    d2 = float(segment1.points[1].distance(intersection_point))
                    d3 = float(segment2.points[0].distance(intersection_point))
                    d4 = float(segment2.points[1].distance(intersection_point))
                    d = np.array([d1,d2,d3,d4]) 
                    if (d < float(endpoint_min_separation)).any():
                        # Discard this pair.
                        # One segment is (almost) touching the other.
                        continue 

                if check_projected_segments_dont_intersect:
                    if checkPointInImage(intersection_point, image_width, image_height):
                        # Discard this pair.
                        # After projecting the segments as lines,
                        # they intersect somewhere on the image.
                        continue

        filtered_pairs.append(pair)

    return filtered_pairs


def getCombinationsOfTwoLists(list1, list2):
    """
    For two sets of Line Segment pairs,
    generate all possible combinations.
    """
    return list(itertools.product(list1, list2))


def getIntersectionLineSegments(segment1, segment2):
    """
    Find the intersection of two line segments,
    by extending them into infinite lines.
    """
    line1 = Line(segment1)
    line2 = Line(segment2)
    intersection_points = line1.intersection(line2)
    intersects = bool(len(intersection_points))
    if intersects:
        intersection_point = intersection_points[0]
        return intersection_point
    # Error, lines do not intersect
    print("WARNING: Horizontal and vertical line segments do not intersect.")
    print("This should not happen!")
    return None


def checkLineSegmentIsAbove(segment1, segment2):
    """
    Check if one line segment is above the other.
    (this assumes the segments are not intersecting)
    """

    # In image coordinates, (+x,+y) is bottom-right corner.
    if (segment1.points[0].y > segment2.points[0].y): return False
    if (segment1.points[0].y > segment2.points[1].y): return False
    if (segment1.points[1].y > segment2.points[0].y): return False
    if (segment1.points[1].y > segment2.points[1].y): return False

    return True


def checkLineSegmentOnLeft(segment1, segment2):
    """
    Check if one line segment is on the left side of the other.
    (this assumes the segments are not intersecting)
    """

    # In image coordinates, (+x,+y) is bottom-right corner.
    if (segment1.points[0].x > segment2.points[0].x): return False
    if (segment1.points[0].x > segment2.points[1].x): return False
    if (segment1.points[1].x > segment2.points[0].x): return False
    if (segment1.points[1].x > segment2.points[1].x): return False

    return True


def getConvexIntersectionPoints_method2(horizontal_segment1, horizontal_segment2, vertical_segment1, vertical_segment2):
    """
    For two pairs of line segments, treat them as
    infinite lines and find the intersection points.

    These 4 points are in a clockwise order that
    represents a convex quadrilateral.
    """

    # Sort the segments in clockwise order
    top_segment = None
    right_segment = None
    bottom_segment = None
    left_segment = None
    if checkLineSegmentIsAbove(horizontal_segment1, horizontal_segment2):
        top_segment = horizontal_segment1
        bottom_segment = horizontal_segment2
    else:
        top_segment = horizontal_segment2
        bottom_segment = horizontal_segment1
    if checkLineSegmentOnLeft(vertical_segment1, vertical_segment2):
        left_segment = vertical_segment1
        right_segment = vertical_segment2
    else:
        left_segment = vertical_segment2
        right_segment = vertical_segment1

    corner_pt1 = getIntersectionLineSegments(left_segment, top_segment)
    corner_pt2 = getIntersectionLineSegments(top_segment, right_segment)
    corner_pt3 = getIntersectionLineSegments(right_segment, bottom_segment)
    corner_pt4 = getIntersectionLineSegments(bottom_segment, left_segment)

    quad_points = [corner_pt1, corner_pt2, corner_pt3, corner_pt4]
    sorted_segments = [top_segment, right_segment, bottom_segment, left_segment]

    return (quad_points, sorted_segments)


def checkSegmentsOnQuad_method2(sorted_segments, corners):
    """ 
    Check if all 4 line segments are within
    the edges of a quadrilateral.

    This assumes that the inputs are already matched.
    """

    if (len(sorted_segments) != 4) or (len(corners) != 4):
       print("ERROR: Expected 4 segments and 4 corners in checkSegmentsOnQuad_method2()")
       sys.exit()

    # Get the 4 edges
    edges = []
    for i in range(3):
        p1 = corners[i]
        p2 = corners[i+1]
        edges.append(Segment(p1, p2))
    p1 = corners[3]
    p2 = corners[0]
    edges.append(Segment(p1, p2))

    for i in range(4):
        if not edges[i].contains(sorted_segments[i]):
            return False
    return True


def getQuads(sets_of_four_segments, image_dims):
    """
    Find quadrilateral shapes.
    """

    image_width, image_height = image_dims

    quads = []
    for i in range(len(sets_of_four_segments)):

        # Determine if 4 line segments represent
        # a valid quadrilateral shape:

        segments = sets_of_four_segments[i]
        horizontal_segment1 = segments[0][0]
        horizontal_segment2 = segments[0][1]
        vertical_segment1 = segments[1][0]
        vertical_segment2 = segments[1][1]

        quad_points, sorted_segments = getConvexIntersectionPoints_method2(horizontal_segment1, horizontal_segment2, vertical_segment1, vertical_segment2)

        if not checkPointsInImage(quad_points, image_width, image_height):
            print("  Bad quad, an intersection point (one corner of the quad) is outside image!")

            # Save debug image
            img = np.copy(input_image)
            drawCrosshairs(img, quad_points)
            drawQuad(img, quad_points)
            suffix = str(i).zfill(2)
            cv2.imwrite("candidate_quad_"+suffix+".jpg", img)

            # Discard this quad.
            # A corner point is outside the image boundary.
            continue

        # Check if each line segment is within one side of the quad.
        #  - The segments can not intersect each other.
        #  - The end of a segment can not extend out past the quad.
        #  - All segments must be contained within one edge of the shape.
        if checkSegmentsOnQuad_method2(sorted_segments, quad_points):
            print("  Good")
            quads.append(quad_points)
        else:
            print("  Bad quad, a line segment is not within the quad")

        # Save debug image
        img = np.copy(input_image)
        drawCrosshairs(img, quad_points)
        drawQuad(img, quad_points)
        suffix = str(i).zfill(2)
        cv2.imwrite("candidate_quad_"+suffix+".jpg", img)
        #cv2.imshow("Quad corners", img)
        #cv2.waitKey()

    return quads


#------------------------------------------------------------------------------#

# Drawing functions:


def drawSegment(image, segment, color):
    """
    Draw a Sympy Line Segment on an OpenCV image.
    """
    thickness = 2
    x1 = int(segment.points[0].x) # should already be int
    y1 = int(segment.points[0].y)
    x2 = int(segment.points[1].x)
    y2 = int(segment.points[1].y)
    cv2.line(image, (x1,y1), (x2,y2), color, thickness)


def drawSegments(image, segments, color=(0,0,255)):
    """
    Draw lines on an OpenCV image.

    Default color is red.
    """
    for segment in segments:
        drawSegment(image, segment, color)


def drawCrosshair(image, point):
    """
    Draw a Sympy Point2D on an OpenCV image
    with a cross marker.
    """
    pt_x = int(round(point.x))
    pt_y = int(round(point.y))
    length = 5
    thickness = 2
    color = (255,0,255) # magenta
    cv2.line(image, (pt_x, pt_y-length), (pt_x, pt_y+length), color, thickness)
    cv2.line(image, (pt_x-length, pt_y), (pt_x+length, pt_y), color, thickness)


def drawCrosshairs(image, points):
    """
    Draw marks on an OpenCV image.
    """
    for point in points:
        drawCrosshair(image, point)


def drawQuad(image, corners, color=(0,255,0)):
    """
    Draw a quadrilateral shape.
    The 4 corner points are Sympy Point2D.
    """
    for i in range(len(corners)-1):
        p1 = corners[i]
        p2 = corners[i+1]
        segment = Segment(p1, p2)
        drawSegment(image, segment, color)
    # Close the polygon
    p1 = corners[len(corners)-1]
    p2 = corners[0]
    segment = Segment(p1, p2)
    drawSegment(image, segment, color)


#------------------------------------------------------------------------------#


if input_image == None:
    print("ERROR: Can't find input image")
    sys.exit()

#cv2.imshow("input_image", input_image)
#cv2.waitKey()


# Line segments sample data
segment1  = Segment(Point(335,120), Point(517,144))
segment2  = Segment(Point(287, 604), Point(558, 619))
segment3  = Segment(Point(323, 131), Point(275, 587))
segment4  = Segment(Point(589, 473), Point(580, 606))
segment5  = Segment(Point(368, 39), Point(489, 108))
segment6  = Segment(Point(53, 286), Point(293, 406))
segment7  = Segment(Point(299, 347), Point(214, 538))
segment8  = Segment(Point(200, 370), Point(149, 528))
segment9  = Segment(Point(6, 446), Point(68, 449))
segment10 = Segment(Point(66, 444), Point(150, 525))
segment11 = Segment(Point(389, 514), Point(518, 644))
segments = [segment1, segment2, segment3, segment4, segment5, segment6, segment7, segment8, segment9, segment10, segment11]


image_width = input_image.shape[1]
image_height = input_image.shape[0]
image_dims = (image_width, image_height)


input_image_with_segments = np.copy(input_image)
drawSegments(input_image_with_segments, segments)
cv2.imshow("input_image_with_segments", input_image_with_segments)
cv2.waitKey()


# Sort the line segments into 2 groups:
horizontal_segments = []
vertical_segments   = []
image_width = input_image.shape[1]
x_axis = Line((0, 0), (image_width, 0))
for segment in segments:
    # Compute the angle of each line segment.
    # Angle is w.r.t. the top edge of the image
    # in a clockwise direction.
    angle = float(x_axis.angle_between(segment))

    # Check 315 to 360 degrees
    if (angle >= 2.0*np.pi-np.pi/4.0) and (angle <= 2.0*np.pi):
        horizontal_segments.append(segment)
    # Check 0 to 45 degrees
    elif (angle >= 0.0) and (angle < np.pi/4.0):
        horizontal_segments.append(segment)
    # Check 135 to 225 degrees
    elif (angle > np.pi-np.pi/4.0) and (angle < np.pi+np.pi/4.0):
        horizontal_segments.append(segment)
    else:
        vertical_segments.append(segment)


# Save debug images
input_image_with_horizontal_segments = np.copy(input_image)
drawSegments(input_image_with_horizontal_segments, horizontal_segments)
cv2.imwrite("segments_horizontal.jpg", input_image_with_horizontal_segments)
input_image_with_vertical_segments = np.copy(input_image)
drawSegments(input_image_with_vertical_segments, vertical_segments)
cv2.imwrite("segments_vertical.jpg", input_image_with_vertical_segments)


# Get all the possible pairs of horizontal line segments:
pairs_of_horizontal_line_segments = getUniquePairs(horizontal_segments, image_dims)
print("Got %d pairs of horizontal line segments" % len(pairs_of_horizontal_line_segments)) # 15 pairs, 10 after filtering

# Get all the pairs of vertical line segments:
pairs_of_vertical_line_segments = getUniquePairs(vertical_segments, image_dims)
print("Got %d pairs of vertical line segments" % len(pairs_of_vertical_line_segments)) # 10 pairs, 6 after filtering


# Save debug images
for i in range(len(pairs_of_horizontal_line_segments)):
    pair = pairs_of_horizontal_line_segments[i]
    segments = [pair[0], pair[1]]
    img = np.copy(input_image)
    drawSegments(img, segments)
    suffix = str(i).zfill(2)
    cv2.imwrite("segment_pairs_horizontal_"+suffix+".jpg", img)
    #cv2.imshow("Pair of segments", img)
    #cv2.waitKey()
for i in range(len(pairs_of_vertical_line_segments)):
    pair = pairs_of_vertical_line_segments[i]
    segments = [pair[0], pair[1]]
    img = np.copy(input_image)
    drawSegments(img, segments)
    suffix = str(i).zfill(2)
    cv2.imwrite("segment_pairs_vertical_"+suffix+".jpg", img)
    #cv2.imshow("Pair of segments", img)
    #cv2.waitKey()


# Get all combinations of 4 line segments:
sets_of_four_line_segments = getCombinationsOfTwoLists(pairs_of_horizontal_line_segments, pairs_of_vertical_line_segments)
print("Got %d potential quadrilaterals" % len(sets_of_four_line_segments)) # = 60


# Find the valid quadrilateral shapes:
quads = getQuads(sets_of_four_line_segments, image_dims)
print("Got %d valid quads" % len(quads))
for i in range(len(quads)):
    img = np.copy(input_image)
    drawQuad(img, quads[i])

    # Save result images
    suffix = str(i).zfill(2)
    cv2.imwrite("quad_"+suffix+".jpg", img)

    title = "Candidate Quad " + str(i)
    cv2.imshow(title, img)
    cv2.waitKey()

【讨论】:

    【解决方案2】:

    如果不限制角度等,任何四条线都可以完成形成四边形。

    可能有错误四边形的图像:

    您可能不想包含像我的示例中显示的黄色那样的四边形。您应该限制角度、最小/最大尺寸、纵横比和允许的完成程度。如果必须添加 90% 的线才能形成一个完整的四边形,这可能不是一个很好的候选对象。

    我担心您将不得不测试每一种可能的线条组合并在它们上应用heuristic 来给它们加分。许多点用于接近 90 度的角度(如果您想要的是矩形)、完整性、接近预期的纵横比等。


    更新

    使用积分系统比仅仅应用严格的规则更有优势。

    • 积分系统可让您评估四边形的质量并选择最好的一个或完全拒绝一个四边形。
    • 一处房产的优良品质有助于抵消另一处房产的劣质。
    • 它允许您为不同的属性赋予不同的权重。

    假设您有一个严格的规则(在伪代码中):

    (angles == 90 +/- 10 degrees) && (line_completeness>50%)
    

    这可行,但可能会导致类似angles == 90 +/- 1 degree) &amp;&amp; (line_completeness == 45%) 的情况。根据规则,这个四边形不会通过,因为线的完整性很差;但是,角度的质量非常出色,仍然使其成为非常好的候选者。

    最好给分。例如,恰好 90 度的角度为 20 分,90 +/- 15 度的角度为 0 分,完整的线条为 10 分,而仅完成 25% 的线条为 0 分。这使得角度比线的完整性更重要,也为没有绝对规则的问题创造了更柔和的条件。

    【讨论】:

    • 是的,我知道这是错误的。这正是重点。问题是:为什么? “四边形”仅表示它有四个边。而已。如果你说“,我正在寻找任何四边形”,那么即使是带有交叉线的四边形也是允许的。可能您只想允许与矩形紧密匹配的那些。
    • 鉴于 OP 使用 Hough 变换,这很可能是计算机视觉问题。的确,最初的问题陈述并不是特别无懈可击,但我假设他们正在尝试检测矩形物体。无论如何,这只是调整评分函数的问题。
    • 我的图像的重点是显示错误的图像以强调约束的必要性。可能一个限制是未完成的线必须至少在四边形内放置一定百分比。蓝色和绿色的未完成的下部线完全位于外面。算法需要明确的规则,因为它不像我们人类那样看到感觉到矩形。
    • 我完全同意你的看法。在编写解决方案时,我必须对隐含的约束做出一些假设。实际上,只要不存在四条完全平行的线的集合,那么该图像中就有 330 个可能的四边形。您的启发式方法几乎就是我在代码中所做的。这是一个有趣的问题!
    • @AgentFire:错误的四边形是故意的。请参阅上面的 cmets。
    【解决方案3】:

    我不使用 C#,因此您必须翻译代码。以下代码使用 Java。我使用包含的测试用例对其进行了测试。我还不知道如何将附件添加到 stackoverflow,所以我在此处包含实际代码。

    有四个类(ShapeFinder、Line、Point 和 Quadrilateral)和一个测试类(ShapeFinderTest):

    ShapeFinder 类:

    package stackoverflow;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class ShapeFinder {
    
      private List<Line> lines;
      private List<Quadrilateral> allQuadrilaterals;
    
      /*
       * I am assuming your segments are in a list of arrays:
       * [{{x1,y1,},{x2,y2}}, {{x1,y1,},{x2,y2}}, {{x1,y1,},{x2,y2}}]
       * You can change this.
       *
       * So basically you call ShapeFinder with a list of your line segments.
       */
      public ShapeFinder(List<Double[][]> allSegments) {
        lines = new ArrayList<Line>(allSegments.size());
        allQuadrilaterals = new ArrayList<Quadrilateral>();
        for (Double[][] segment : allSegments) {
          addSlopeInterceptForm(segment);
        }
      }
    
      /**
       * You call this function to compute all possible quadrilaterals for you.
       */
      public List<Quadrilateral> completeQuadrilaterals() {
        for (int w = 0; w < lines.size(); w++) {
          for (int x = w + 1; x < lines.size(); x++) {
            for (int y = x + 1; y < lines.size(); y++) {
              for (int z = y + 1; z < lines.size(); z++) {
                addQuadrilateral(w, x, y, z);
              }
            }
          }
        }
        return allQuadrilaterals;
      }
    
      //assume {{x1,y1,},{x2,y2}}
      private void addSlopeInterceptForm(Double[][] s) {
        double x1 = s[0][0];
        double y1 = s[0][1];
        double x2 = s[1][0];
        double y2 = s[1][1];
        double m = (y1 - y2) / (x1 - x2);
        double b = y2 - m * x2;
    
        if (isInfinityOrNaN(m)) {
          m = Double.NaN;
          b = x1;
        }
    
        lines.add(new Line(m, b));
      }
    
      /*
       * Given four lines, this function creates a quadrilateral if possible
       */
      private void addQuadrilateral(int w, int x, int y, int z) {
        Point wx = intersect(w, x);
        Point wy = intersect(w, y);
        Point wz = intersect(w, z);
        Point xy = intersect(x, y);
        Point xz = intersect(x, z);
        Point yz = intersect(y, z);
    
        if (notNull(wx) && notNull(xy) && notNull(yz) && notNull(wz) && isNull(wy) && isNull(xz)) {
          allQuadrilaterals.add(new Quadrilateral(wx, xy, yz, wz));
        }
      }
    
      private Point intersect(int c, int d) {
        double m1 = lines.get(c).slope;
        double b1 = lines.get(c).intercept;
        double m2 = lines.get(d).slope;
        double b2 = lines.get(d).intercept;
    
        double xCor, yCor;
        if ((isInfinityOrNaN(m1) && !isInfinityOrNaN(m2)) || (!isInfinityOrNaN(m1) && isInfinityOrNaN(m2))) {
          xCor = isInfinityOrNaN(m1) ? b1 : b2;
          yCor = isInfinityOrNaN(m1) ? m2 * xCor + b2 : m1 * xCor + b1;;
        } else {
          xCor = (b2 - b1) / (m1 - m2);
          yCor = m1 * xCor + b1;
        }
    
        if (isInfinityOrNaN(xCor) || isInfinityOrNaN(yCor)) {
          return null;
        }
        return new Point(xCor, yCor);
      }
    
      private boolean isInfinityOrNaN(double d){
        return Double.isInfinite(d)||Double.isNaN(d);
      }
    
      private boolean notNull(Point p) {
        return null != p;
      }
    
      private boolean isNull(Point p) {
        return null == p;
      }
    }
    

    线类:

    package stackoverflow;
    
    public class Line {
    
      double slope;
      double intercept;
    
      public Line(double slope, double intercept) {
        this.slope = slope;
        this.intercept = intercept;
      }
    }
    

    点类:

    package stackoverflow;
    
    class Point {
    
      double xCor;
      double yCor;
    
      public Point(double xCor, double yCor) {
        this.xCor = xCor;
        this.yCor = yCor;
      }
    
      public String toString(){
        return "("+xCor+","+yCor+")";
      }
    }
    

    四边形类:

    package stackoverflow;
    
    public class Quadrilateral {
    
      private Point w, x, y, z;
    
      public Quadrilateral(Point w, Point x, Point y, Point z) {
        this.w = w;
        this.x = x;
        this.y = y;
        this.z = z;
      }
    
      public String toString() {
        return "[" + w.toString() + ", " + x.toString() + ", " + y.toString() + ", " + z.toString() + "]";
      }
    }
    

    单元测试:

    package stackoverflow;
    
    import java.util.ArrayList;
    import java.util.List;
    import org.junit.Test;
    
    public class ShapeFinderTest {
    
      @Test
      public void testCompleteQuadrilaterals() {
        List<Double[][]> lines = new ArrayList<>();
        lines.add(new Double[][]{{2., 5.}, {6., 5.}});
        lines.add(new Double[][]{{2., 1.}, {2., 5.}});
        lines.add(new Double[][]{{2., 1.}, {6., 1.}});
        lines.add(new Double[][]{{6., 5.}, {6., 1.}});
        lines.add(new Double[][]{{0., 0.}, {5., 1.}});
        lines.add(new Double[][]{{5., 5.}, {10., 25.}});
        ShapeFinder instance = new ShapeFinder(lines);
        List<Quadrilateral> result = instance.completeQuadrilaterals();
    
        for (Quadrilateral q : result) {
          System.out.println(q.toString());
        }
      }
    }
    

    【讨论】:

    • 如果您发布一些结果图片可能会有所帮助。
    【解决方案4】:

    从示例中,我认为问题更多地是查找所有四边形,其每条边都包含一条线。从所提供的解释来看,这一点根本不清楚。

    下面是一些相当容易实现的伪代码。现在只是为了创建一个有效的数据结构来防止 O(N^4) 复杂性。也许可以按位置或梯度对线条进行排序。

    i,j,k,l 如下:

       l
     |---|
    j|   |k
     |---|
       i
    

    extendIntersect 只是一个函数,它将 2 条线扩展到无穷大(或您选择的任何边界)并返回它们相交的点,这在数学上很容易做到。

    onLine 如果点位于一条线上,则返回 true。

    onSameSide 如果两个点位于直线的同一侧,则返回 true

    for (Line i = lines[0]:lines[lineCount])
      for (Line j = lines[1]:lines[lineCount])
        Point ijIntersect = extendIntersect(i, j)
        if (ijIntersect == NULL || onLine(ijIntersect, i) || onLine(ijIntersect, j))
          continue;
        for (Line k = lines[2]:lines[lineCount])
          Point ikIntersect = extendIntersect(i, k)
          if (ikIntersect == NULL || onLine(ikIntersect, i) || onLine(ikIntersect, k) ||
              onSameSide(ijIntersect, ikIntersect, i)) continue
          for (Line l = lines[3]:lines[lineCount])
            Point jlIntersect = extendIntersect(j, l)
            Point klIntersect = extendIntersect(k, l)
            if (jlIntersect == NULL || onLine(jlIntersect, j) || onLine(jlIntersect, l) ||
                klIntersect == NULL || onLine(klIntersect, k) || onLine(klIntersect, l) ||
                onSameSide(jlIntersect, ijIntersect, j) ||
                onSameSide(klIntersect, ikIntersect, k)) continue
            printQuad(ijIntersect, ikIntersect, klIntersect, jlIntersect)
    

    Drew Noakes 建议的某种错误检查也可能是个好主意。

    【讨论】:

      【解决方案5】:

      在 11 条线段的情况下,您有 330 种方式选择四个线段。您可以确定每个组合构成四边形的可能性,并以此方式评分。

      可以使用 Hough 变换检测除线条以外的形式,但由于累加器空间需要多于两个维度,因此更难以可视化。可以在三个维度(midX、midY、半径)中找到圆,在四个维度中找到椭圆(我相信)。我不确定你需要多少参数来模拟一个四边形,我相信当你得到高于三个维度时,霍夫变换的性能开始下降。累加器空间变得如此之大,以至于噪声比显着增加。

      这里有一个related question,可能会给你一些有趣的答案。

      让我们知道你的进展情况!


      编辑

      我今天尝试解决这个问题,uploaded my solution to GitHub。这里的代码太多了。

      这是显示输出的屏幕截图:

      我采取的解决方案基本上是我在此编辑之前描述的。

      1. 找出四行的所有组合
      2. 找出这四行的所有排列
      3. 评估这四条线形成四边形的可能性
      4. 选择最佳匹配

      评估通过计算粗略的错误分数来进行。这是两种不同类型错误的总和:

      1. 每个角与 90 度的偏差(我使用所有四个角的误差平方和)
      2. 当线段在线段内相交时,它可能不是有效角

      第二种类型的错误可能会以更可靠的方式确定。有必要为您的样本数据集找到解决方案。

      我没有尝试过其他数据集。它可能需要一些调整以使其更强大。我试图避免使用太多参数,以便直接适应特定环境。例如,控制对遮挡的敏感度,如您的示例图像所示。

      它在我的笔记本电脑上大约 160 毫秒内找到了解决方案。但是我没有进行任何性能优化。如果您需要更接近实时地运行,我希望找到组合/排列的方法可以得到显着优化,就像计算机视觉实验经常发生的情况一样。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-08-26
        • 1970-01-01
        • 2012-04-06
        • 2012-11-10
        • 1970-01-01
        相关资源
        最近更新 更多