Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identify whether image is a circle #237

Closed
marckohlbrugge opened this issue Jun 14, 2020 · 9 comments
Closed

Identify whether image is a circle #237

marckohlbrugge opened this issue Jun 14, 2020 · 9 comments
Labels

Comments

@marckohlbrugge
Copy link

For a web app I'm building, I'm trying to figure out whether a given logo image has the shape of a circle.

I've come up with two different strategies, but I'm stuck moving forward with both.

The first strategy is to use hough circle transform to detect all circles in the image. If there's a circle that's roughly the size of the whole image, that probably means the image is indeed of a circular logo.

Here's how far I've got so far:

bytes = open(logo_url, &:read)
im = Vips::Image.new_from_buffer bytes, ""

im.colourspace(:b_w).flatten.bandsplit.first.hough_circle(scale: 1, min_radius: 5, max_radius: 500).write_to_memory.unpack('I_*')

I'm not sure what values to use for scale, min_radius, and max_radius. I think I can somehow use them to get only big circles that are around the image size. The .write_to_memory.unpack('I_*') part I get from #138 where @jcupitt explains it returns a matrix of "votes" for each pixel.

Any pointers on how to move forward from here?

The second strategy I came up with is applying a circular mask. And then comparing that to the original image. If the images remain identical, that means the part 'outside' the circular mask are unused and there's a high likelihood the image is a circle (or has a lot of whitespace).

I'm not sure how to approach this, although I did find VIPS::Mask.


Curious to hear your ideas and whether there's any relevant documentation I can read?

@Nakilon
Copy link
Contributor

Nakilon commented Jun 15, 2020

Can we see input examples?

@Nakilon
Copy link
Contributor

Nakilon commented Jun 15, 2020

The last one is tricky. Is it supposed to not match because the hexagon is outside of the circle?

@jcupitt
Copy link
Member

jcupitt commented Jun 15, 2020

You're right, the docs for hough_circle don't explain what the axes represent. I've tried to clarify it.

Here's a circle detector for your logos:

#!/usr/bin/ruby

require 'vips'

image = Vips::Image.new_from_file(ARGV[0])

# we need a one-band image of just the edges for circle detection
edges = image.colourspace(:b_w).flatten.canny(precision: :integer) 

# search for circles roughly the size of the image, so radius of half the
# diameter of the largest circle
radius_target = [image.width, image.height].min / 2
radius_margin = 20  
detect = edges.hough_circle(min_radius: radius_target - radius_margin, 
                            max_radius: radius_target + radius_margin)

# look for the (x, y) with the peak, then at that point, find the radius
strength, opts = detect.max(x: true, y: true)
x = opts["x"]
y = opts["y"]
bands = detect.getpoint(x, y)
radius_detected = bands.each_with_index.max[1] + radius_target - radius_margin

puts "strength = #{strength}, x = #{x}, y = #{y}, radius = #{radius_detected}"

For this PNG I see:

logo3

$ ./detect_circle.rb ~/pics/logo3.png
strength = 559.0, x = 100, y = 100, radius = 89

But for this one:

logo4

I see:

$ ./detect_circle.rb ~/pics/logo4.png
strength = 167.0, x = 104, y = 118, radius = 80

That's just picking the strongest circle, so a strong circle off centre could hide a weak centred circle. You'd probably want to crop the hough output before searching for a peak. You could blur or rank filter as well to reduce noise.

jcupitt added a commit to libvips/libvips that referenced this issue Jun 15, 2020
we were not actually saying what the output axes represented

see libvips/ruby-vips#237
@jcupitt
Copy link
Member

jcupitt commented Jun 15, 2020

... I meant to say, you need to edge-detect before hough transforms, and canny is probably the best one. Photographic images will need some noise filtering too (threshold, median, smooth, etc.). Hough output will often also need some filtering, perhaps a rank filter to find local peaks, or morphology to remove isolated peaks.

Your PNG logos are very clean, of course, so you can skip most of this extra processing.

@marckohlbrugge
Copy link
Author

Wow, thank you. This is extremely helpful.

It seems like the strength variable is related to the size of the image and/or radius, correct? So if I do something like score = strength / radius I can figure out a threshold for images to reach in order to be considered a circle.

@jcupitt
Copy link
Member

jcupitt commented Jun 16, 2020

strength is corrected for radius, so it should just be a confidence score. I'd test it on some typical images and pick a threshold, eg. 300. Test cx/cy for being near the centre too.

@marckohlbrugge
Copy link
Author

I ran the code on different sizes of the same image (the blue globe icon attached above) and got these results:

ruby circle.rb globe.png
=> strength = 559.0, x = 100, y = 100, radius = 89
ruby circle.rb globe100px.png
=> strength = 262.0, x = 50, y = 50, radius = 45
ruby circle.rb globe500px.png
=> strength = 209.0, x = 256, y = 256, radius = 231

As you can see the strength value for the first, original, image is significantly higher. It seems like they all pertain to the same circle though. (central and with a radius just under half the width)

Any idea why that might be the case?

@jcupitt
Copy link
Member

jcupitt commented Jun 16, 2020

I'd guess because the circles don't quite line up. libvips is using a fast int approximation of a circle for the hough transform, and perhaps the logo is using something different.

You could try doing a slight gaussblur after canny and again after hough_circle. Your detected strengths will drop, but the result should be more stable.

@jcupitt jcupitt closed this as completed Jan 31, 2021
@libvips libvips locked and limited conversation to collaborators Jan 31, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
Projects
None yet
Development

No branches or pull requests

3 participants