colour-checker-detection
colour-checker-detection copied to clipboard
[FEATURE]: More Robust Square Detection In Segmentation
Description
In segementation.py when filtering for squares/swatches contours
the original code reads:
# Filtering squares/swatches contours.
swatches = []
for contour in contours:
curve = cv2.approxPolyDP(
contour, 0.01 * cv2.arcLength(contour, True), True
)
if minimum_area < cv2.contourArea(curve) < maximum_area and is_square(
curve
):
swatches.append(
as_int_array(cv2.boxPoints(cv2.minAreaRect(curve)))
)
suggested way:
def angle_cos(p0, p1, p2):
d1, d2 = (p0 - p1).astype('float'), (p2 - p1).astype('float')
return abs(np.dot(d1, d2) / np.sqrt(np.dot(d1, d1) * np.dot(d2, d2)))
# Filtering squares/swatches contours.
swatches = []
for contour in contours:
curve = cv2.approxPolyDP(
contour, 0.01 * cv2.arcLength(contour, True), True
)
if len(curve) == 4 and minimum_area < cv2.contourArea(curve) < maximum_area and cv2.isContourConvex(curve):
cnt = curve.reshape(-1, 2)
max_cos = np.max([angle_cos( cnt[i], cnt[(i+1) % 4], cnt[(i+2) % 4] ) for i in range(4)])
if max_cos < 0.1:
swatches.append(as_int_array(cv2.boxPoints(cv2.minAreaRect(curve))))
From my little testing it seems to help prevent erroneous contours from being detected on the passport color-checker eg:
WITHOUT SUGGESTION:

WITH SUGGESTION:

Hi @mexicantexan,
Thanks this is interesting! Do you have an explanation as to why it is removing the contours better? Is it also possible to share your image please?
Cheers,
Thomas
@KelSolaar Honestly, I'm not 100% sure as to why it works better. The mathematics behind it are pretty simple though. Basically we're filtering the contours based on the number of sides equalling 4, ensuring that the min/max of each side is within our suggested range, and ensuring that the sum of all the angles makes a square with each side being within a margin of 90 degrees (that's where the 0.1 comes from, increase it to allow other quadrilaterals to come into play). The code is based on OpenCV's finding squares python script.
For a bulletproof method that doesn't rely on fine-tuned params, I have implemented a robust version of your filtering method. The only catch is that on larger images (HD and larger), it can take a couple of seconds for it to filter and find everything. Would you like me to share that?
For the image, I can definitely share the image with you! It is rather large though (15MB) what would be the best way to share it?
The mathematics behind it are pretty simple though.
Yes, they did make sense, I unroll them and understood what the 0.1 tolerance was. I was mostly wondering if you knew how come it was excluding some contours better than cv2.matchShapes.
Would you like me to share that?
Certainly happy! :)
It is rather large though (15MB) what would be the best way to share it?
https://wetransfer.com ?
I have updated the segmentation code and it should not produce doubled contours anymore.