如何检查点是否在一组轮廓内

时间:2017-11-16 18:57:33

标签: opencv computer-vision feature-detection sift opencv-contour

enter image description here

大家好。上面的图像是两个图像的总和,其中我进行了特征匹配并绘制了所有匹配点。我还在第一张图像中找到了pcb部件的轮廓(左半图像-3轮廓)。问题是,我怎么能只绘制第一张图像中那些轮廓内的匹配点而不是这个蓝色的混乱?我使用的是python 2.7和opencv 2.4.12。

我为opencv 2.4.12写了一个绘制匹配原因的函数,没有任何实现的方法。如果我没有包含某些内容,请告诉我。提前谢谢!

import numpy as np
import cv2

def drawMatches(img1, kp1, img2, kp2, matches):

    # Create a new output image that concatenates the two images
    # (a.k.a) a montage
    rows1 = img1.shape[0]
    cols1 = img1.shape[1]
    rows2 = img2.shape[0]
    cols2 = img2.shape[1]

    # Create the output image
    # The rows of the output are the largest between the two images
    # and the columns are simply the sum of the two together
    # The intent is to make this a colour image, so make this 3 channels
    out = np.zeros((max([rows1,rows2]),cols1+cols2,3), dtype='uint8')

    # Place the first image to the left
    out[:rows1,:cols1] = np.dstack([img1, img1, img1])

    # Place the next image to the right of it
    out[:rows2,cols1:] = np.dstack([img2, img2, img2])

    # For each pair of points we have between both images
    # draw circles, then connect a line between them
    for mat in matches:

        # Get the matching keypoints for each of the images
        img1_idx = mat.queryIdx
        img2_idx = mat.trainIdx

        # x - columns
        # y - rows
        (x1,y1) = kp1[img1_idx].pt
        (x2,y2) = kp2[img2_idx].pt

        # Draw a small circle at both co-ordinates
        # radius 4
        # colour blue
        # thickness = 1
        cv2.circle(out, (int(x1),int(y1)), 4, (255, 0, 0), 1)
        cv2.circle(out, (int(x2)+cols1,int(y2)), 4, (255, 0, 0), 1)

        # Draw a line in between the two points
        # thickness = 1
        # colour blue
        cv2.line(out, (int(x1),int(y1)), (int(x2)+cols1,int(y2)), (255,0,0), 1)


    # Show the image
    cv2.imshow('Matched Features', out)
    cv2.imwrite("shift_points.png", out)
    cv2.waitKey(0)
    cv2.destroyWindow('Matched Features')

    # Also return the image if you'd like a copy
    return out


img1 = cv2.imread('pic3.png', 0) # Original image - ensure grayscale
img2 = cv2.imread('pic1.png', 0) # Rotated image - ensure grayscale

sift = cv2.SIFT()

# find the keypoints and descriptors with SIFT
kp1, des1 = sift.detectAndCompute(img1,None)
kp2, des2 = sift.detectAndCompute(img2,None)

# Create matcher
bf = cv2.BFMatcher()

# Perform KNN matching
matches = bf.knnMatch(des1, des2, k=2)

# Apply ratio test
good = []
for m,n in matches:
    if m.distance < 0.75*n.distance:
       # Add first matched keypoint to list
       # if ratio test passes
       good.append(m)

# Show only the top 10 matches - also save a copy for use later
out = drawMatches(img1, kp1, img2, kp2, good)

1 个答案:

答案 0 :(得分:1)

根据你的要求我假设你的意思是你有一些封闭的轮廓概述了你想要绑定数据点对的区域。

这对于多边形轮廓来说相当简单,对于更复杂的曲线需要更多数学,但解决方案是相同的。

您从有问题的点到无穷远处绘制一条线。大多数人画出一条到+ x无穷大的线,但任何方向都有效。如果存在奇数个线交点,则该点位于轮廓内。

看到这篇文章: http://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/

对于点对,只有两个点在轮廓内的对完全在轮廓内。对于具有凹面的复杂轮廓形状,如果您还想测试点之间的线性路径没有穿过轮廓,则只需要两点之间的线段执行类似的测试,如果有任何直线交点直接点之间的路径穿过轮廓外。

修改

由于您的轮廓是矩形,因此更简单的方法就足以确定您的点是否在矩形内。

如果你的矩形是轴对齐的(它们是直的而不是旋转的),那么你可以使用你的值为top,left和bottom,right来检查。

指点A =上,左,点B =下,右,点C =您的测试点。

我假设一个基于图像的坐标系,其中0,0是图像的左上角,宽度,高度是右下角。 (我正在用C#写作)

bool PointIsInside(Point A, Point B, Point C)
{
    if (A.X <= C.X && B.X >= C.X   &&   A.Y <= C.Y && B.Y >= C.Y)
        return true;
    return false;
}

如果您的矩形不是轴对齐的,那么您可以执行四个半空间测试来确定您的点是否在矩形内。

设点A =顶部,左侧,点B =底部,右侧,双W =宽度,双H =高度,双N =旋转角度,C点=测试点。

对于轴对齐的矩形,可以通过取矢量(1,0),乘以宽度,并将该矢量添加到Top,Left来计算Top,Right。对于Bottom,Right我们取向量(0,1),乘以高度,然后加到Top,Right。

(1,0)相当于角度0处的单位矢量(长度为1)。类似地,(0,1)是角度为90度的单位矢量。这些向量也可以被认为是线指向的方向。这也意味着这些相同的向量可以用于从底部,从左到底,从右到左,从左到右,从左到左。

我们需要在提供的角度使用不同的单位向量。要做到这一点,我们只需要取角度的余弦和正弦。

让向量X =从顶部,左到右,向右的方向,向量Y =从顶部,从右到底,向右的方向。

我在这个例子中使用度数角度。

Vector X = new Vector();
Vector Y = new Vector();
X.X = Math.Cos(R);
X.Y = Math.Sin(R);
Y.X = Math.Cos(R+90);
Y.Y = Math.Sin(R+90);

由于我们从Top,Left开始,我们可以通过简单地将两个向量添加到Top,Left来找到Bottom,Right

Point B = new Point();
B = A + X + Y;

我们现在想要使用点积作为测试点进行半空间测试。前两个测试将使用测试点,而Top,Left,其他两个将使用测试点,而Bottom,Right。

半空间测试本质上是基于方向性的。点是在给定方向的前面,后面还是垂直?我们有两个方向我们需要,但它们是基于矩形的左上角的方向,而不是图像的完整空间,所以我们需要从顶部,左边到相关点获得矢量,另一个来自底部,右边,因为这是我们测试的两点。

这很容易计算,因为它只是目的地 - 原点。

设矢量D =顶部,左边是测试点C,矢量E =底部,右边是测试点。

Vector D = C - A;
Vector E = C - B;

点积是两个向量的x1 * x2 + y1 * y2。如果结果是正的,则两个方向的绝对角度小于90度,或者大致朝向相同的方向,零的结果意味着它们是垂直的。在我们的例子中,它意味着测试点直接位于我们正在测试的矩形的一侧。小于零意味着绝对角度大于90度,或者它们大致相反的方向。

如果一个点位于矩形内部,那么左上角的点积将是> = 0,而来自右下角的点积将是&lt; = 0.本质上,测试点更靠近右下角从左上角进行测试,但是当我们已经在右下方时采取相同的方向,它将会离开,返回顶部,左侧。

double DotProd(Vector V1, Vector V2)
{
    return V1.X * V2.X + V1.Y * V2.Y;
}

所以我们的测试结束为:

if( DotProd(X, D) >= 0 && DotProd(Y, D) >= 0    &&    DotProd(X, E) <= 0 && DotProd(Y, E) <= 0)

然后该点在矩形内。对两个点都执行此操作,如果两者都为真,则该行在矩形内。