Hi, I've drawn one line in my poor code T_T, but I don't have time now to transform it into the iterative one: draw parallel lines along the shorter side to divide the rect into 2, 4, 8, ... parts, hope this can help you a bit:)
I'll try to spare time to turn it into a iterative one.
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("./a.png")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thr = cv2.threshold(gray, 127, 255, cv2.THRESH_OTSU)
_, contours, _ = cv2.findContours(thr, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
small_cnts = [i for i in contours if cv2.contourArea(i) < 100]
cv2.fillPoly(thr, small_cnts, 0)
_, cnts, _ = cv2.findContours(thr, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key=lambda x: cv2.contourArea(x))
cnts.pop()
img_cp = img.copy()
[cv2.drawContours(img_cp, contours, i, (255, 0, 0), thickness=3) for i in range(len(contours))]
plt.imshow(cv2.cvtColor(img_cp, cv2.COLOR_BGR2RGB))
plt.show()
cnt_0 = cnts[0]
cnt_1 = cnts[1]
rect_0 = cv2.minAreaRect(cnt_0)
rect_1 = cv2.minAreaRect(cnt_1)
box_0 = cv2.boxPoints(rect_0).astype(np.int0)
box_1 = cv2.boxPoints(rect_1).astype(np.int0)
cv2.drawContours(img_cp, [box_0], 0, (0, 255, 0), 5)
_ = cv2.drawContours(img_cp, [box_1], 0, (0, 255, 0), 5)
plt.imshow(cv2.cvtColor(img_cp, cv2.COLOR_BGR2RGB))
plt.show()
# get the 2 points at two shortest borders
p = box_0[0]
min_dist = 100000
min_idx = -1
for i in range(1, box_0.shape[0]):
if (box_0[i][0] - p[0])**2 + (box_0[i][1] - p[1])**2 < min_dist:
min_idx = i
min_dist = (box_0[i][0] - p[0])**2 + (box_0[i][1] - p[1])**2
cen_0 = [np.mean((box_0[min_idx][0], p[0])), np.mean((box_0[min_idx][1], p[1]))]
t_lst = list(range(4))
t_lst.remove(0)
t_lst.remove(min_idx)
cen_1 = [np.mean((box_0[t_lst[0]][0], box_0[t_lst[1]][0])), np.mean((box_0[t_lst[0]][1], box_0[t_lst[1]][1]))]
# Then we get the main direction
if not cen_1[0] - cen_0[0]:
slope = 0
else:
main_direction = (cen_1[1] - cen_0[1]) / (cen_1[0] - cen_0[0])
if not cen_1[1] - cen_0[1]:
# slope doesn't exist
pass
else:
slope = 1/main_direction
# get sec_max_point
max_dist = 0
max_idx = -1
for i in range(1, box_0.shape[0]):
if (box_0[i][0] - p[0])**2 + (box_0[i][1] - p[1])**2 > max_dist:
max_idx = i
max_dist = (box_0[i][0] - p[0])**2 + (box_0[i][1] - p[1])**2
sec_max_dist = 0
sec_max_idx = -1
tt_lst = list(range(4))
for i in range(1, box_0.shape[0]):
if i == max_idx:
continue
if (box_0[i][0] - p[0])**2 + (box_0[i][1] - p[1])**2 > sec_max_dist:
sec_max_idx = i
sec_max_dist = (box_0[i][0] - p[0])**2 + (box_0[i][1] - p[1])**2
tt_lst.remove(0)
tt_lst.remove(sec_max_idx)
# draw cross lines
line1_points = [box_0[0], box_0[sec_max_idx]]
line1_middle = [int(np.mean((line1_points[0][0], line1_points[1][0]))),
int(np.mean((line1_points[0][1], line1_points[1][1])))]
line2_points = [box_0[tt_lst[0]], box_0[tt_lst[1]]]
line2_middle = [int(np.mean ...
(more)
for accurate measurement first you need a camera calibration and second either an object with known size or a fixed working distance. Doing this you are able to compare the real world with the virtual one. Otherwise its not possible.
@VxW Considering that the about conditions are met. I just want to draw small lines(as seen on ruler in mm) on two sides of the inside the bounding box.
you can iterate along the lines using LineIterator (see here), calculate the normal vector and draw the lines using cv::lines. Additionally you will need a scale parameter of your iteration steps (distance between the small ruler lines)