Description
Hey,
I've been tinkering around the problem of being able to detect the color-checker when its shot was taken, so that the approximate rectangle of the checker is distorted.
I am unsure if the detection of such a contour is currently possible with this package, but I would like to contribute in order to enable such a functionality.
As the masking of the checker should stay fixed, the idea is to warp the detected contour to fit the mask.
When the contour is warped even the size of the mask can stay fixed.
This can be done as follows:
# the dimensions are somewhat arbitrary but should fit the aspect ratio of the checker
def correct_distortion(distorted_cnt, x_dim, y_dim, img):
desired_cnt = [[0,0],[x_dim,0],[x_dim,y_dim],[0, y_dim]]
M = cv2.getPerspectiveTransform(np.float32(distorted_cnt), np.float32(desired_cnt))
return cv2.warpPerspective(img, M,(x_dim,y_dim))
As the checker orientation matters, it is necessary to ensure that the points of the detected contour are ordered in the same way as the new coordinates e.g.
My attempt to ensure such an ordering are the following functions:
def order_clockwise(cnt):
centroid = contour_centroid(cnt)
angles = [math.atan2(c[1]-centroid[0][1], c[0]-centroid[0][0])*180//np.pi for corner in cnt]
clockwise_contours = [c for c, a in sorted(zip(unpacked_cnt, angles), key=lambda pair: pair[1])]
return clockwise_contours
def order_cnt(cnt):
clockwise_contour = order_clockwise(cnt)
lowest_point_index = np.argmax([pt[0][1] for pt in clockwise_contour])
next_index = (lowest_point_index+1) % len(clockwise_contour)
previous_index = lowest_point_index-1
lowest_point = clockwise_contour[lowest_point_index]
next_point = clockwise_contour[next_index]
previous_point = clockwise_contour[previous_index]
next_diff_x = np.abs(lowest_point[0][0] - next_point[0][0])
next_diff_y = np.abs(lowest_point[0][1] - next_point[0][1])
previous_diff_x = np.abs(lowest_point[0][0] - previous_point[0][0])
previous_diff_y = np.abs(lowest_point[0][1] - previous_point[0][1])
anti_clockwise_angle = math.atan2(next_diff_y, next_diff_x) * 180 //np.pi
clockwise_angle = math.atan2(previous_diff_y, previous_diff_x) * 180 //np.pi
# Lower line is already aligned in the image
if next_diff_y == 0 or previous_diff_y == 0:
next_angle = previous_angle = "Aligned"
# "Falling" to the right, Less rotation needed to align when rotating clockwise
if anti_clockwise_angle > clockwise_angle:
first_index = next_index
# "Falling" to the left, Less rotation needed to align when rotating anti-clockwise
if anti_clockwise_angle == "Aligned" or anti_clockwise_angle <= clockwise_angle:
first_index = next_index+1 if next_index+1 < len(clockwise_contour) else 0
if previous_diff_y == 0:
first_index -= 1
return clockwise_contour[first_index:] + clockwise_contour[:first_index]
This works under the assumption that the checker is not flipped but was recorded as the aspect ratio suggests. It is required to perform the ordering before the wrapping.
I've attached a notebook, with a demonstration of the described pipeline
Demo.zip