3

I am new here and a little bit newbie in programming.

I have one question. I have picture of Sun in bmp file and 16 bit. The picture look as white circle with black backround.

enter image description here

I want to find a circle and identify its center in x,y coordinates.

I have this script

import cv
import numpy as np




orig = cv.LoadImage('sun0016.bmp')

grey_scale = cv.CreateImage(cv.GetSize(orig), 8, 1)
processed = cv.CreateImage(cv.GetSize(orig), 8, 1)

cv.Smooth(orig, orig, cv.CV_GAUSSIAN, 5, 5)
cv.CvtColor(orig, grey_scale, cv.CV_RGB2GRAY)
cv.Erode(grey_scale, processed, None, 10)
cv.Dilate(processed, processed, None, 10)
cv.Canny(processed, processed, 5, 70, 3)
cv.Smooth(processed, processed, cv.CV_GAUSSIAN, 15, 15)

storage = cv.CreateMat(orig.width, 1, cv.CV_32FC3)


cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 1, 16.0, 10, 140)

for i in range(0, len(np.asarray(storage))):
    print "circle #%d" %i
    Radius = int(np.asarray(storage)[i][0][2])
    x = int(np.asarray(storage)[i][0][0])
    y = int(np.asarray(storage)[i][0][1])
    center = (x, y)
    print x,y

    cv.Circle(orig, center, 1, cv.CV_RGB(0, 255, 0), 1, 8, 0)
    cv.Circle(orig, center, Radius, cv.CV_RGB(255, 0, 0), 1, 8, 0)

    cv.Circle(processed, center, 1, cv.CV_RGB(0, 0, 0), -1, 8, 0)
    cv.Circle(processed, center, Radius, cv.CV_RGB(255, 0, 0), 3, 8, 0)

cv.ShowImage("sun0016", orig)
cv.ShowImage("processed", processed)
cv_key = cv.WaitKey(0)

And when I run this I find edge of Sun which is circle with center but very inaccurately. Pls know you setting of parameters HoughCircles module for precise search circles. Thanks

Mailerdaimon
  • 6,003
  • 3
  • 35
  • 46
Franta Konopnik
  • 189
  • 2
  • 9
  • 20

3 Answers3

1

The main problem here is finding a good range for your radius. You may have a look at your picture and guess the Radius.

From the Picture you have given I would guess 180 - 220 would be a good range.

Your code would look like:

cv.HoughCircles(processed, storage, cv.CV_HOUGH_GRADIENT, 1, 16.0, 180, 220)

Just try to find good Values for minRadius and maxRadius and this should work fine.

Mailerdaimon
  • 6,003
  • 3
  • 35
  • 46
  • Thats why i wrote that i guessed it, i didn´t worked it out, that is up to you. I only saw the source image. If one of your several preprocessing steps changed the radius i didn´t account for it. Either you trial and error good values or you print out the input image for hough circle and measure the range. If the range is correct and you don´t have multiple circles houghlines is pretty accurate. – Mailerdaimon Nov 04 '13 at 14:26
  • I dont understand. I thought that modul houghcircles found Sun automaticly. – Franta Konopnik Nov 04 '13 at 14:48
  • It can, but if you need a very accurate output you have to choose a good radius range. If you choose your radius range wrong houghcircles can still find circles but they may be displaced or just not the circles you were looking for. – Mailerdaimon Nov 04 '13 at 14:50
  • I see. And this range must I find out with experiment? Is there an execution method for find range? – Franta Konopnik Nov 04 '13 at 15:02
  • By experiment or you measure it in the image that you pass to the houghcircle Method. Just count the radius of your object in pixel. – Mailerdaimon Nov 04 '13 at 15:04
  • Ok the radius is 227 pixels. But if this range is 220 - 230 script not work..I dont understand – Franta Konopnik Nov 04 '13 at 15:51
1

here is solution of my problem

import numpy as np
import cv2

im = cv2.imread('sun0016.bmp')
height, width, depth = im.shape
print height, width, depth
thresh = 132
imgray = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(imgray,(5,5),0)
edges = cv2.Canny(blur,thresh,thresh*2)
contours, hierarchy = cv2.findContours(edges,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
cv2.drawContours(im,contours,-1,(0,255,0),-1)

#centroid_x = M10/M00 and centroid_y = M01/M00
M = cv2.moments(cnt)
x = int(M['m10']/M['m00'])
y = int(M['m01']/M['m00'])
print x,y
print width/2.0,height/2.0
print width/2-x,height/2-y


cv2.circle(im,(x,y),1,(0,0,255),2)
cv2.putText(im,"center of Sun contour", (x,y), cv2.FONT_HERSHEY_SIMPLEX, 1, (0,0,255))
cv2.circle(im,(width/2,height/2),1,(255,0,0),2)
cv2.putText(im,"center of image", (width/2,height/2), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,0,0))
cv2.imshow('contour',im)
cv2.waitKey(0)
Franta Konopnik
  • 189
  • 2
  • 9
  • 20
1

Thought I would chime in with an alternative solution in case anyone stumbles upon this question in the future.

The following function uses cv2.inRange instead of cv2.Canny, and cv2.minEnclosingCircle instead of cv2.moments. It selects the largest contour found by cv2.findContours by measuring the radius of candidates' minimum enclosing circle. This filtering helps reject false positives from e.g. watermarks or dust, but depending on your requirements you might want to go about this step differently or omit it entirely.

The function returns both the x,y coordinates as well as the radius of the detected disk, a requirement for the project I was working on.

import cv2


def find_disk(img, threshold=10):
    """Finds the center and radius of a single solar disk present in the supplied image.

    Uses cv2.inRange, cv2.findContours and cv2.minEnclosingCircle to determine the centre and 
    radius of the solar disk present in the supplied image.

    Args:
        img (numpy.ndarray): greyscale image containing a solar disk against a background that is below `threshold`.
        threshold (int): threshold of min pixel value to consider as part of the solar disk

    Returns:
        tuple: center coordinates in x,y form (int) 
        int: radius
    """
    if img is None:
        raise TypeError("img argument is None - check that the path of the loaded image is correct.")

    if len(img.shape) > 2:
        raise TypeError("Expected single channel (grayscale) image.")

    blurred = cv2.GaussianBlur(img, (5, 5), 0)
    mask = cv2.inRange(blurred, threshold, 255)
    img_mod, contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    # Find and use the biggest contour
    r = 0
    for cnt in contours:
        (c_x, c_y), c_r = cv2.minEnclosingCircle(cnt)
        # cv2.circle(img, (round(c_x), round(c_y)), round(c_r), (255, 255, 255), 2)
        if c_r > r:
            x = c_x
            y = c_y
            r = c_r

    # print("Number of contours found: {}".format(len(contours)))
    # cv2.imwrite("mask.jpg", mask)
    # cv2.imwrite("circled_contours.jpg", img)

    if x is None:
        raise RuntimeError("No disks detected in the image.")

    return (round(x), round(y)), round(r)


if __name__ == "__main__":
    image = cv2.imread("path/to/your/image.jpg")
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    center, radius = find_disk(img=gray, threshold=20)

    print("circle x,y: {},{}".format(center[0], center[1]))
    print("circle radius: {}".format(radius))

    # Output the original image with the detected disk superimposed
    cv2.circle(image, center, radius, (0, 0, 255), 1)
    cv2.rectangle(image, (center[0] - 2, center[1] - 2), (center[0] + 2, center[1] + 2), (0, 0, 255), -1)
    cv2.imwrite("disk_superimposed.jpg", image)

I have left in some commented debug statements that may come in handy if you find the need to tinker with this further.

You might want to use a higher threshold if your images include a lot of glare.