Robbie Barrat
Robbie Barrat

Reputation: 520

python + cv2 - determine radius of bright spot in image

I already have code that can detect the brightest point in an image (just gaussian blurring + finding the brightest pixel). I am working with photographs of sunsets, and right now can very easily get results like this:

enter image description here

My issue is that the radius of the circle is tied to how much gaussian blur i use - I would like to make it so that the radius reflects the size of the sun in the photo (I have a dataset of ~500 sunset photos I am trying to process).

Here is an image with no circle:

enter image description here I don't even know where to start on this, my traditional computer vision knowledge is lacking.. If I don't get an answer I might try and do something like calculate the distance from the center of the circle to the nearest edge (using canny edge detection) - if there is a better way please let me know. Thank you for reading

Upvotes: 0

Views: 4196

Answers (2)

fmw42
fmw42

Reputation: 53081

Here is one way to get a representative circle in Python/OpenCV. It finds the minimum enclosing circle.

  • Read the input
  • Crop out the white on the right side
  • Convert to gray
  • Apply median filtering
  • Do Canny edge detection
  • Get the coordinates of all the white pixels (canny edges)
  • Compute minimum enclosing circle to get center and radius
  • Draw a circle with that center and radius on a copy of the input
  • Save the result

Input:

enter image description here

import cv2
import numpy as np

# read image as grayscale
img = cv2.imread('sunset.jpg')
hh, ww = img.shape[:2]

# shave off white region on right side
img = img[0:hh, 0:ww-2]

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

# median filter
median = cv2.medianBlur(gray, 3)

# do canny edge detection
canny = cv2.Canny(median, 100, 200)

# get canny points
# numpy points are (y,x)
points = np.argwhere(canny>0)

# get min enclosing circle
center, radius = cv2.minEnclosingCircle(points)
print('center:', center, 'radius:', radius)

# draw circle on copy of input
result = img.copy()
x = int(center[1])
y = int(center[0])
rad = int(radius)
cv2.circle(result, (x,y), rad, (255,255,255), 1)

# write results
cv2.imwrite("sunset_canny.jpg", canny)
cv2.imwrite("sunset_circle.jpg", result)

# show results
cv2.imshow("median", median)
cv2.imshow("canny", canny)
cv2.imshow("result", result)
cv2.waitKey(0)

Canny Edges:

enter image description here

Resulting Circle:

enter image description here

center: (265.5, 504.5) radius: 137.57373046875

Alternate

Fit ellipse to Canny points and then get the average of the two ellipse radii for the radius of the circle. Note a slight change in the Canny arguments to get only the top part of the sunset.

import cv2
import numpy as np

# read image as grayscale
img = cv2.imread('sunset.jpg')
hh, ww = img.shape[:2]

# shave off white region on right side
img = img[0:hh, 0:ww-2]

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

# median filter
median = cv2.medianBlur(gray, 3)

# do canny edge detection
canny = cv2.Canny(median, 100, 250)

# transpose canny image to compensate for following numpy points as y,x
canny_t = cv2.transpose(canny)

# get canny points
# numpy points are (y,x)
points = np.argwhere(canny_t>0)

# fit ellipse and get ellipse center, minor and major diameters and angle in degree
ellipse = cv2.fitEllipse(points)
(x,y), (d1,d2), angle = ellipse
print('center: (', x,y, ')', 'diameters: (', d1, d2, ')')

# draw ellipse
result = img.copy()
cv2.ellipse(result, (int(x),int(y)), (int(d1/2),int(d2/2)), angle, 0, 360, (0,0,0), 1)

# draw circle on copy of input of radius = half average of diameters = (d1+d2)/4
rad = int((d1+d2)/4)
xc = int(x)
yc = int(y)
print('center: (', xc,yc, ')', 'radius:', rad)
cv2.circle(result, (xc,yc), rad, (0,255,0), 1)

# write results
cv2.imwrite("sunset_canny_ellipse.jpg", canny)
cv2.imwrite("sunset_ellipse_circle.jpg", result)

# show results
cv2.imshow("median", median)
cv2.imshow("canny", canny)
cv2.imshow("result", result)
cv2.waitKey(0)

Canny Edge Image:

enter image description here

Ellipse and Circle drawn on Input:

enter image description here

Upvotes: 3

Davis
Davis

Reputation: 602

Use Canny edge first. Then try either Hough circle or Hough ellipse on the edge image. These are brute force methods, so they will be slow, but they are resistant to non-circular or non-elliptical contours. You can easily filter results such that the detected result has a center near the brightest point. Also, knowing the estimated size of the sun will help with computation speed.

You can also look into using cv2.findContours and cv2.approxPolyDP to extract continuous contours from your images. You could filter by perimeter length and shape and then run a least squares fit, or Hough fit.

EDIT

It may be worth trying an intensity filter before the Canny edge detection. I suspect it will clean up the edge image considerably.

Upvotes: 1

Related Questions