0

enter image description here

Hello to everyone. The above image is sum of two images in which i did feature matching and draw all matching points. I also found the contours of the pcb parts in the first image (half left image-3 contours). The question is, how could i draw only the matching points that is inside those contours in the first image instead this blue mess? I'm using python 2.7 and opencv 2.4.12.

I wrote a function for draw matches cause in opencv 2.4.12 there isn't any implemented method for that. If i didn't include something please tell me. Thank you in advance!

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)
Jeru Luke
  • 20,118
  • 13
  • 80
  • 87
Eugene Moush
  • 71
  • 2
  • 7

1 Answers1

1

Based on what you are asking I am assuming you mean you have some sort of closed contour outlining the areas you want to bound your data point pairs to.

This is fairly simple for polygonal contours and more math is required for more complex curved lines but the solution is the same.

You draw a line from the point in question to infinity. Most people draw out a line to +x infinity, but any direction works. If there are an odd number of line intersections, the point is inside the contour.

See this article: http://www.geeksforgeeks.org/how-to-check-if-a-given-point-lies-inside-a-polygon/

For point pairs, only pairs where both points are inside the contour are fully inside the contour. For complex contour shapes with concave sections, if you also want to test that the linear path between the points does not cross the contour, you perform a similar test with just the line segment between the two points, if there are any line intersections the direct path between the points crosses outside the contour.

Edit:

Since your contours are rectangles, a simpler approach will suffice for determining if your points are inside the rectangle.

If your rectangles are axis aligned (they are straight and not rotated), then you can use your values for top,left and bottom,right to check.

Let point A = Top,Left, point B = Bottom,Right, and point C = your test point.

I am assuming an image based coordinate system where 0,0 is the left,top of the image, and width,height is the bottom right. (I'm writing in 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;
}

if your rectangle is NOT axis aligned, then you can perform four half-space tests to determine if your point is inside the rectangle.

Let Point A = Top,Left, Point B = Bottom,Right, double W = Width, double H = Height, double N = rotation angle, and Point C = test point.

for an axis aligned rectangle, Top,Right can be calculated by taking the vector (1,0) , multiplying by Width, and adding that vector to Top,Left. For Bottom,Right We take the vector (0,1), multiply by height, and add to Top,Right.

(1,0) is the equivalent of a Unit Vector (length of 1) at Angle 0. Similarly, (0,1) is a unit vector at angle 90 degrees. These vectors can also be considered the direction the line is pointing. This also means these same vectors can be used to go from Bottom,Left to Bottom,Right, and from Top,Left to Bottom,Left as well.

We need to use different unit vectors, at the angle provided. To do this we simply need to take the Cosine and Sine of the angle provided.

Let Vector X = direction from Top,Left to Top,Right, Vector Y = direction from Top,Right to Bottom,Right.

I am using angles in degrees for this example.

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);

Since we started with Top,Left, we can find Bottom,Right by simply adding the two vectors to Top,Left

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

We now want to do a half-space test using the dot product for our test point. The first two test will use the test point, and Top,Left, the other two will use the test point, and Bottom,Right.

The half-space test is inherently based on directionality. Is the point in front, behind, or perpendicular to a given direction? We have the two directions we need, but they are directions based on the top,left point of the rectangle, not the full space of the image, so we need to get a vector from the top,left, to the point in question, and another from the bottom, right, since those are the two points we test against.

This is simple to calculate, as it is just Destination - Origin.

Let Vector D = Top,Left to test point C, and Vector E = Bottom,Right to test point.

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

The dot product is x1 * x2 + y1*y2 of the two vectors. if the result is positive, the two directions have an absolute angle of less than 90 degrees, or are roughly going in the same direction, a result of zero means they are perpendicular. In our case it means the test point is directly on a side of the rectangle we are testing against. Less than zero means an absolute angle of greater than 90 degrees, or they are roughly going opposite directions.

If a point is inside the rectangle, then the dot products from top left will be >= 0, while the dot products from bottom right will be <= 0. In essence the test point is closer to bottom right when testing from top left, but when taking the same directions when we are already at bottom right, it will be going away, back toward top,left.

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

and so our test ends up as:

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

then the point is inside the rectangle. Do this for both points, if both are true then the line is inside the rectangle.

Draco
  • 107
  • 3
  • I have a big set of points given from sift and feature matching and i want to draw only those inside the contours that i found. I saw somewhere about the method that you suggest but i couldn't understand how to implement it. I will check the article that you attached and try to make it. If you have the time and know how to do it place some example code maybe that would help me out. Either way thank you! – Eugene Moush Nov 17 '17 at 22:52
  • By the way my contours are rectangles – Eugene Moush Nov 17 '17 at 22:53