Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fit theoretical dartboard in image containing dartboard

Tags:

python

opencv

I have the following image containing a dartboard

enter image description here

After processing the image looks as follows:

enter image description here

In addition, I have a function that creates a theoretical dartboard:

import cv2
import numpy as np

def draw_dartboard():
        IMG = np.ones((400, 400), 'uint8') * 255
        center = (int(IMG.shape[0] // 2), int(IMG.shape[1] // 2))
        size_dartboard = int(340)
        r_board = int(170)
        r_db = int(6.35)
        r_sb = int(15.9)
        r_doubles = int(162)
        r_triples = int(99)
        width_rings = int(8)

        cv2.circle(IMG, center, r_doubles + width_rings, (0,0,0), -1)
        cv2.circle(IMG, center, r_doubles, (255,255,255), -1)
        cv2.circle(IMG, center, r_triples + width_rings, (0,0,0), -1)
        cv2.circle(IMG, center, r_triples, (255,255,255), -1)
        
        thetas_min = np.radians([(18 * t - 9) for t in range(20)])
        thetas_max = np.radians([(18 * t + 9) for t in range(20)])
        
        for idx, (theta_min, theta_max) in enumerate(zip(thetas_min, thetas_max)):            
            if (idx % 2) == 0: 
                x_min = int(center[0] + r_board * np.cos(theta_min))
                y_min = int(center[1] + r_board * np.sin(theta_min))
                x_max = int(center[0] + r_board * np.cos(theta_max))
                y_max = int(center[1] + r_board * np.sin(theta_max))
                cv2.fillPoly(IMG, np.array([(center, (x_min,y_min), (x_max,y_max))]), (0,0,0))
           
        cv2.circle(IMG, center, r_sb, (0,0,0), -1)
        
        return IMG

The output of this image looks as follows:

enter image description here

How can I “fit” the theoretical dartboard in the real image? Clearly, there is a mismatch in orientation and scale. What's the best way to do this?

like image 211
HJA24 Avatar asked Oct 24 '25 02:10

HJA24


2 Answers

You can register your dartboard image (i.e. source image) to the one you processed (i.e. destination image) by using affine transformations.

Here is my approach, and the outcome.

import cv2
import matplotlib.pyplot as plt
import numpy as np

# read images and remove matplotlib axes
src = cv2.imread('source.png',0)
src = src[20:-30,40:-20]

dest = cv2.imread('dest.png',0)
dest = dest[40:-40,40:-40]

# find matching points manually
dest_pts = np.array([[103,29],[215,13],[236,125]]).astype(np.float32) # x,y
src_pts = np.array([[19,175],[145,158],[176,284]]).astype(np.float32) #x,y

# calculate the affine transformation matrix
warp_mat = cv2.getAffineTransform(src_pts, dest_pts)

# get the registered source image
warp_dst = cv2.warpAffine(src, warp_mat, (dest.shape[1], dest.shape[0]))



fig,ax = plt.subplots(1,3)
ax[0].imshow(src,'gray')
ax[0].scatter(src_pts[:,0],src_pts[:,1],s=1,c='r')
ax[0].set_title('src')
ax[1].imshow(dest,'gray')
ax[1].scatter(dest_pts[:,0],dest_pts[:,1],s=1,c='r')
ax[1].set_title('dest')
ax[2].imshow(warp_dst,'gray')
ax[2].set_title('registered src')
plt.savefig('result.png')


fig, ax = plt.subplots(1)
ax.imshow(dest,'gray')
ax.imshow(warp_dst,cmap='jet',alpha=0.5)
plt.savefig('overlayed_result.png')
# plt.show()

result

In order to calculate affine transformation matrix, you will need 3 matching points on both images. I highlighted the points I chose on both images. FYI, you can develop a way to automate finding matching points, let us know in your question if you need that.

registered image overlay on the destination image

like image 63
Prefect Avatar answered Oct 25 '25 16:10

Prefect


As you have already done the image processing, I will take it from there. So just to be clear, this is the image I will be working with (I cropped out the matplotlib axises, as I'm sure they aren't present in your actual image):

enter image description here

The concept is really simple:

  1. Find the bounding box of the contour of the target.

  2. With the bounding box, we can find the radius of the target by selecting the greatest among the dimensions (width and height) of the bounding box, and dividing it by 2.

  3. With the radius of the target and the top-left corner coordinates of the target (returned when finding the bounding box of the target), we can find the center of the target with the expressions x + r and y + h - r.

With the radius of the target, you can scale your theoretical target accordingly, and with the center of the target, you can draw your theoretical target at the right coordinates.

Here is how the code goes, where Image.png is the above image. Note that I only draw one circle onto the image; the rest of them can be drawn on using the same way, with just some added scaling:

import cv2
import numpy as np

img = cv2.imread("Image.png")
img_processed = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
contours, _ = cv2.findContours(img_processed, cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
cnt = sorted(contours, key=cv2.contourArea)[-2]

x, y, w, h = cv2.boundingRect(cnt)
r = max(w, h) // 2
center_x = x + r
center_y = y + h - r
cv2.circle(img, (center_x, center_y), r, (0, 255, 0), 5)
cv2.imshow("Image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Output:

enter image description here

Note that at this line:

cnt = sorted(contours, key=cv2.contourArea)[-2]

I am getting the contour with the second-greatest area, as the one with the greatest area would be the border of the image.

like image 21
Ann Zen Avatar answered Oct 25 '25 15:10

Ann Zen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!