Enhancing corner detection of lamp with OpenCV

Viewed 318

I'm using the following code to detect the brightly illuminated lamp. The illumination might vary. I'm using the following code to detect the same.

img = cv2.imread("input_img.jpg")
rgb = img.copy()
img_grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
while True:

    th3 = cv2.adaptiveThreshold(img_grey, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \
                                cv2.THRESH_BINARY, 11, 2)

    cv2.imshow("th3",th3)

    edged = cv2.Canny(th3, 50, 100)
    edged = cv2.dilate(edged, None, iterations=1)
    edged = cv2.erode(edged, None, iterations=1)

    cv2.imshow("edge", edged)

    cnts = cv2.findContours(edged.copy(), cv2.RETR_TREE,
                            cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    areaArray = []

    for i, c in enumerate(cnts):
        area = cv2.contourArea(c)
        areaArray.append(area)
    sorteddata = sorted(zip(areaArray, cnts), key=lambda x: x[0], reverse=True)

    thirdlargestcontour = sorteddata[2][1]
    x, y, w, h = cv2.boundingRect(thirdlargestcontour)
    cv2.drawContours(rgb, thirdlargestcontour, -1, (255, 0, 0), 2)

    cv2.rectangle(rgb, (x, y), (x + w, y + h), (0, 255, 0), 2)

    cv2.imshow("rgb", rgb)
    if cv2.waitKey(1) == 27:
        break

The above code works but,

  1. It only gives the rectangle that encompasses the lamp. How do I get the four corner points of the lamp precisely?
  2. How can I improve detection? at the moment I'm picking the third-largest contour which does not guarantee that it will always be the lamp as the environment poses challenge?

enter image description here

ApproxPolydp works when the contour is complete but if the contour is incomplete, ApproxPolydp is not returning the proper coordinate. for instance in the following image the approxpolydp returns a wrong coordinates.

enter image description here

1 Answers

Here is one way to do that in Python/OpenCV.

  • Read the input image and convert to grayscale
  • Use adaptive thresholding to get a thick outline of the lamp region
  • Find the contours
  • Filter the contours on area to remove extraneous regions and keep only the larger of the two (inner and outer contours of thresholded region)
  • Get the perimeter
  • Fit the perimeter to a polygon, which should be a quadrilateral with the right choice of arguments.
  • Draw the contour (red) and polygon (blue) over a copy of the input image as the result

Input:

enter image description here

import cv2
import numpy as np

# load image
img = cv2.imread("lamp.jpg")

# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# threshold image
thresh = cv2.adaptiveThreshold(gray, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 11, 10)
thresh = 255 - thresh

# find contours
cntrs = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]

# Contour filtering -- remove small objects and those that are too large
# Keep the larger of the two contours (inner and outer contours from thresh)
area_thresh = 0
for c in cntrs:
    area = cv2.contourArea(c)
    if area > 200 and area > area_thresh:
        big_contour = c
        area_thresh = area

# draw big_contour on image in red and polygon in blue and print corners
results = img.copy()
cv2.drawContours(results,[big_contour],0,(0,0,255),1)
peri = cv2.arcLength(big_contour, True)
corners = cv2.approxPolyDP(big_contour, 0.04 * peri, True)
cv2.drawContours(results,[corners],0,(255,0,0),1)
print(len(corners))
print(corners)

# write result to disk
cv2.imwrite("lamp_thresh.jpg", thresh)
cv2.imwrite("lamp_corners.jpg", results)

cv2.imshow("THRESH", thresh)
cv2.imshow("RESULTS", results)
cv2.waitKey(0)
cv2.destroyAllWindows()


Thresholded Image:

enter image description here

Result Image:

enter image description here

Corner Coordinates:

[[[233 145]]

 [[219 346]]

 [[542 348]]

 [[508 153]]]


Related