Question:
How do I make cv2.line() more accurate?

Problem:

To make it simple, I'm working with one contour, which is a very close approximation to a parallelogram. I need to draw a line through the contour such that the line fits under the contour perfectly.


Here's a minimum reproducible example:

import numpy as np

import cv2

from math import dist


# setup stuff

filler_img = np.zeros((1600, 1600), dtype=np.uint8)

cnt = np.array([[490, 885], [648, 1065], [636, 1201], [486, 1026]])  # [[733, 712], [897, 886], [688, 1006], [528, 828]]

c1, c2, c3 = cnt[:-1]

moments = cv2.moments(cnt)

center = (int(moments["m10"] / moments["m00"]), int(moments["m01"] / moments["m00"]))  # (x, y)


# calculating pass-through line

y_to_x_ratio = (c2[1] - c3[1]) / (c3[0] - c2[0])

point1 = (0, int(y_to_x_ratio*center[0] + center[1]))

point2 = (1600, int(-y_to_x_ratio*(1600-center[0]) + center[1]))

cv2.line(filler_img, point1, point2, 100, thickness=int(dist(c1, c2)))   # problems with thcikness argument


# filling circles at c1, c2, and c3

disp_img = cv2.drawContours(cv2.cvtColor(filler_img, cv2.COLOR_GRAY2BGR), [cnt], -1, (255, 255, 255), 5)

cv2.circle(disp_img, c1, 20, (0, 0, 255), -1)  # red

cv2.circle(disp_img, c2, 20, (0, 255, 0), -1)  # green

cv2.circle(disp_img, c3, 20, (255, 0, 0), -1)  # blue

imshow(disp_img)


Note: imshow() is a function that shows images on Jupyter Notebook - it's implementation is below:

def imshow(*imgs: cv2.Mat) -> None:

    def helper(img: cv2.Mat) -> None:

        plt.axis('off')

        plt.grid(False)

        plt.imshow(cv2.cvtColor(img, cv2.COLOR_RGB2BGR))

        plt.show()

    for img in imgs:

        helper(img)


This is what the code produces:

As you can see in the code, I've got the calculations correct for the angle of the line. It is based off of corners in the contour, which I've also labeled on the output image: c1: red, c2: green, c3: blue. The angle is simply found using c2 and c3 (the drawn line is parallel to the line between c2 and c3), and I found the thickness to be the distance between c1 and c2 (perpendicular to the drawn line). However, the line drawn is obviously more thick than it should be.

More interestingly, this issue goes away with a different contour. This is the result of the same code, but with the contour in the comment in the cnt = ... line:

As you can see, this is a much better fit of the contour. How can I consistently draw lines that fill the area as accurately as possible?


Solution:

The distance marked with the straight line is what I needed to find. Here is a calculation for thickness which works:


thickness = int(dist(c1, c2) * abs(sin(atan2(c2[1] - c3[1], c3[0] - c2[0]) - atan2(c2[1] - c1[1], c1[0] - c2[0]))))


Suggested blogs:

>Authentication with Vue 3 and Firebase

>Build a minimal API using ASP.NET Core with Android Studio

>Build a minimal ASP.Net Core API with Android Studio for Mac

>Complete guide on Life Cycle of Angular Component

>Complete guide to Perform crud operation in angular using modal popup

>Create a Vue.js application with CLI Services


Ritu Singh

Ritu Singh

Submit
0 Answers