I have been trying to use pictures of a given spotlight and the light it casts on a surface in order to detect the circle within the spotlight and determine the center and diameter of the circle. Upon working with it I have run into a few issues.
Using a binary threshold works for most images, but there are some, such as the one shown below where it doesn’t (Top Left). The thresholding is adjusting to the wrong part of the spotlight (Top Right), and even trying to use adaptive thresholding didn’t seem to do much for the issue.
There are many images that don’t have a completely filled-in circle (Bottom Left) and are somewhat pixelated, which means that the threshold is being applied to the incorrect part of the image (Bottom Right)
To get the outputs you see on the right, all I have done is:
import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt
img = cv.imread('images/2023_05_16_15_49_12 spot-ok.jpg', cv.IMREAD_GRAYSCALE)
assert img is not None, "file could not be read, check with os.path.exists()"
ret,thresh1 = cv.threshold(img,127,255,cv.THRESH_BINARY)
cv.imshow("Image", thresh1)
cv.waitKey(0)
The images that I am working with are images of spotlights, and the point I was trying to make was that they are captured at different points in time. The objective is to detect the circle in the image of the shown spotlight and figure out its features. As for the multiple shapes, that is simply how the images turned out, and I need to get rid of the excess (the half circular thing).
Decreasing the threshold helped with the second issue, but the first one still persists, and decreasing 127 to 75 comes to something like this, where it is still failing to threshold effectively.
how do you take these pictures? where do you take them? with what equipment?
what causes these spotlights?
why do these spotlights exist?
why do they move? how do they move?
why is there more than one “light” in these pictures?
you needn’t understand why these questions are relevant. they are. if you think you answered any of those already, I’d appreciate you considering that the given answer didn’t address the point of the question.
These are not my images, so I am unsure about the specifics of how the formation of the image and why the inconsistencies within them exist. However, there are 3 subsets of images that represent the distinct groups of images that naturally form from their differences, if that would be of any help.
Try some median filtering on your input images before thresholding. Also you will almost certainly want some sort of dynamic threshold - there are many ways to skin that cat, but I’d start by playing around with the parameters you feed to the built-in adaptive threshold function - I suspect you will be able to address the issue with the half moon by trying different window sizes.
Focus on getting the noise smoothed out on the circle - maybe a few passes of a small-ish median threshold would be good enough. Then figure out your adaptive threshold parameters. If you have reasonable bounds on the size of the circle in the image, you could always count the number of white pixels after thresholding and compare it to what you expect…that could be used to drive your threshold value.
I’m not a big fan of HoughCircles, but it might be good enough for your purposes. If not, I can share my experience with other circle finding methods…but first things first.
Could you clarify what you mean by adjusting the window sizes, because playing with the “blocksize” and “constant” parameters I’m passing in, the half-moon issue is persisting regardless of my adjusting both parameters and testing various values for both.
The half moon issue, I had imagined, was that (because it was brighter) it was driving the threshold value too high, which resulted in you losing the feature of interest. I wasn’t suggesting you could get rid of the half moon, but rather you could retain the feature you wanted. (Which it looks like you have done).
The image you have above is a good starting point, I think. I have some ideas about how to turn that into what you want. But first I think it is worth noting that you run the risk of over-fitting. That is, if your test cases aren’t representative of the range of input images you will need to process, you might build an image processing pipeline that works well for the image above, but isn’t flexible/robust enough to handle the actual input you could end up seeing.
Having said that, here are my thoughts:
It really would help if you could post the original input images. So far we have a low res screenshot to work from, which is helpful but not ideal. Similarly images showing intermediate results (after each step of the pipeline) are helpful.
What you have might be a good enough starting point, but I’d try to get the speckle / noise down before thresholding. You might be able to get rid of the jagged edges and interior noise with some additional morphology steps, but you also migh tbe able to avoid it altogether with better early steps.
Have you tried to run HoughCircles on that image above? You will probably end up with multiple responses, but it could be relatively easy to choose the right one (based on radius, for example.)
You’d still have to get your image processing figured out so you can feed a list of points (presumed to be on the circle) to the fitting algorithm, etc.
4. Your half moon artifact might pollute your results, but there are ways to manage that. For example you could fit a circle to your edges / contour, and use the resulting (biased) circle to create a mask which you apply to the original image, effectively blocking out most/all of your artifact. Then you process the masked image to get a more accurate circle. A few iterations could get good results. Alternately you could do some sort of RANSAC approach - fitting circles to some random subset of the edge / contour points. If your contours are disconnected, you might do just fine fitting a circle to the “best” contour (largest, most circular, one with a radius closes to the expected radius, etc.)
Cleaning up the image using the median blur seems to be working better now, but the circle detection is clearly still flawed. Hopefully the new included images can be of more help for you to assist me in the right direction.
Looking at the 4 images you have, I’d think you could expect to get something working for images 1,2 and 4, but image 3 is going to be challenging. I tend to take the view that “if I can find the circle, surely we can get a computer to find the circle” - you know, the optimistic approach. The pragmatist in me thinks that the 3rd image is too challenging, at least at this stage, so I’d set it aside for later.
A few more thoughts:
load the images into Gimp or Photoshop (or an interactive image processing toolkit if you have access to one) and play around with various things - threshold, filtering, edge detection, etc.) - maybe an interactive tool like this will help you get a feel for what works / what doesn’t, order of operations etc. It looks like you are working in Python, so maybe you already have a good enough way to test / iterate on things? I just find it helpful sometimes to do some basic investigation in Gimp because of the interactive nature of it. I use the threshold function a lot just to get a sense of what I’m dealing with, for example. Just a thought.
If I find some time I’ll run your images through a circle detection routine I use regularly. I might have to tweak things a bit to work with your images, but if I can get results I’ll post them. Unfortunately I can’t share the code since I don’t own it, but if it works I might be able to give some pointers of how to approach it.
Does your solution have to be fully automated, or is some amount of user input acceptable? Alternately, are there any assumptions you can make about the location of the spot in the image? For example, in all of the input images you provided, it looks like the spot is always on the left side of the image. Is that always true? Can you narrow it down even further?
Setting aside the third image is fine for now. I will try running it through some photo editing software, and see what works. Also, an automated solution is desired. Also, I am not completely sure about the location of the images remaining constant, as these are not my own images, but for all of the images that I have available to me right now, they seem to all be in the same general location.
After continuing to work with this, I am still unable to detect the circles using HoughCircles. Are there any other circle detection methods I should be trying, or do I have to try and manipulate the image more in order for HoughCircles to work?
import cv2 as cv
import numpy as np
img = cv.imread('image.jpg', cv.IMREAD_GRAYSCALE)
img = cv.medianBlur(img,33)
assert img is not None, "file could not be read, check with os.path.exists()"
image = cv.adaptiveThreshold(img,255,cv.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv.THRESH_BINARY,73,2)
blur = cv.blur(image,(5,5))
circles = cv.HoughCircles(blur,cv.HOUGH_GRADIENT,1,20,
param1=50,param2=30,minRadius=30)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv.circle(circles,(i[0],i[1]),i[2],(0,255,0),2)
# draw the center of the circle
cv.circle(circles,(i[0],i[1]),2,(0,0,255),3)
cv.imshow('Objects Detected',blur)
cv.waitKey(0)
It’s pretty difficult to make any suggestions without seeing more images / results using different parameters. I would read the documentation and figure out which parameters might be important for the problems you are seeing. For example, you previously posted an image / result that had a number of smaller circles in it. It’s possible that one of those circles was too close to the center of the circle that you actually want, and because of the min_distance parameter you are using, the larger circle got filtered. It seems like those smaller circles are bogus, and could be eliminated just based on their diameter. If that’s the case, I’d suggest using a larger value for minRadius, so you don’t even get those responses.
If you try a range of different values for different parameters and still aren’t having any luck, I would suggest posting some result images along with the parameters you were using for that trial.