Detecting a very indistinct triangle on a dial using OpenCV

Viewed 102

So I have a temperature box where I am trying to pinpoint the coordinate location of a small triangle on each temperature dial. Here are the examples of the box with slight variations:

[

I have been able to isolate each dial, get their outlines and centers. I then have an algorithm that will generate an angle measure from the center point and then the eventually found point on the triangle. However, I have been unable, so to speak, "find" solely the triangle using OpenCV. I've been able to outline it and such but cannot figure out how to isolate just it's lines. I have tried multiple shape detection and edge detection blocks of code but have had no luck because its so lightly raised from the actual dial. If I can just get a point on the dial that would be good enough even.

1 Answers

There are several possible approaches you can try in order to find the direction of the dial. In this answer I will try it with classic contour detection. However a well trained ML model can be much more robust and reliable in different lighting conditions. But of course it is more effort to set it up.

Let's say that you already have isolated the dial and know its radius and center. Starting from there the straight forward approach would be:

  • Prepare the image for thresholding:
    • If the image is of low resolution as in our case, scale it up by some reasonable factor
    • If the image is of high resolution, blur it to reduce noise
    • Convert it to grayscale
  • Apply adaptiveThresholding or Canny, in this case use the first one
  • Only keep areas that are of interest:
    • In this case only keep the features in a circular range where the triangle is supposed to be
    • In this case only keep the contour with the largest area
  • Derive the result:
    • In this case just get the centroid of the largest contour

Code:

import cv2
import numpy as np


# read image, scale it up by some factor and apply adaptive thresholding
img = cv2.imread("img_red.jpg")
h, w, _ = img.shape
f = 8
img = cv2.resize(img, (w * f, h * f))
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(gray, 255,
                               cv2.cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                               cv2.THRESH_BINARY_INV, 71, 5)
cv2.imwrite("thresh.png", thresh)

# only examine circle where the triangle is supposed to be
mask = np.zeros_like(thresh)
cv2.circle(mask, (int(w * f / 2), int(h * f / 2)), int(w * f / 3), 255, int(w * f / 6))
thresh = cv2.bitwise_and(thresh, mask)
cv2.imwrite("thresh_mask.png", thresh)

# get contours, derive contour with largest area and get centroid
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

if contours:
    m = max([(c, cv2.contourArea(c)) for c in contours], key=lambda i: i[1])[0]

    M = cv2.moments(m)

    if M['m00'] > 0:
        x = round(M['m10'] / M['m00'])
        y = round(M['m01'] / M['m00'])

        # draw small red circle at centroid
        cv2.circle(img, (x, y), 2 * f, (0, 0, 255), f)

cv2.imwrite("out.png", img)

Results:

results

Related