Issue
I need to generate circular masks that represent receptive fields in the human eye. The main problem is that I'm not sure how to calculate the percentage of area covered by pixels that are partially outside the circle. For example, consider this simple code for generating a circular mask:
def circle(size=19, r=7, x_offset=0, y_offset=0):
x0 = y0 = size // 2
x0 += x_offset
y0 += y_offset
y, x = np.ogrid[:size, :size]
y = y[::-1]
return ((x - x0)**2 + (y-y0)**2)<= r**2
which generates this circle:
As you can see the output is binary and if the circle covers at least 50% of the area, its weight is set to 1 and if it's less than 50% coverage, it's set to 0. Here is an overlay, showing it more clearly:
Another common method of generating circles that appear more smooth is to use a 2D gaussian:
def gaussian2d(size=19, sigma = 3, amplitude=1, x_offset=0, y_offset=0):
x = np.arange(0, size, 1, float)
y = x[:,np.newaxis][::-1]
x0 = y0 = size // 2
x0 += x_offset
y0 += y_offset
return amplitude*np.exp(-(((x-x0)**2 /(2*sigma**2)) + ((y-y0)**2 /(2*sigma**2))))
resulting in an output that looks like this:
This solves the problem with the binary output (weights set to 1 or 0), but the problem is that the weights start decreasing even for pixels that are 100% covered by the circle, which is not correct.
ideally, the mask should look something like this (this is hand-drawn):
The pixels that are 100% covered by the circle are set to 1 and the ones that are partially covered, are set to the percentage area covered by the circle, for example:
The efficiency (both in terms of runtime and memory usage) isn't all that important, I just need a way to calculate the percentage of the area of the pixel, that is covered by the circle. Accuracy isn't super important either and a 1% error should be fine.
Solution
A box and a circle may overlap or not. If the four corners are inside the circle, then that's 100% of your "percentage of the area" target.
So the first step is calculate the four distances from the corners to the center of the circle.
di = sqrt((xi-xc)^2 + (yi-yc)^2)
If all di
are greater or equal to the radius R
of the circle then box is outside the circle. Similary, if all corners verify di <= R
then the cell is totally inside the circle.
The rest of cases have one to three corners inside the circle.
Let's see this "one corner outside" case:
The point is calculate the coordinates of points P,Q
.
Because you know A-B is horizontal you can get the xQ
coordinate by using the y
coordinate (same for A and B)
xQ= xc + sqrt(R^2-(yA-yc)^2)
where xc,yc
are the coordinates of the center of the circle. Notice that yQ = yA = yB
Be aware you can get two values, depending on the sign of the sqrt you use. Take the one that is xA <= xQ <= xB
.
Similary, using xA
you can find xP
.
In this moment you know all coordinates.
Pay attention to straight P,Q
. The area below it is easy, just some triangles. Or the total area of the cell deducing the triangle P,A,Q
.
The area between the straight and the circumference is the area of a circular sector (which you can browse the Internet to find it) minus a triangle.
You'll need to find the angle of this sector. For that, I advise to use atan2
instead of atan
, because the former gives an angle in (0, 2pi) range. And you must work with the substraction atan2(f1)-atan2(f2)
normalized into (0,pi) range.
You can draw other cases (e.g. three corners out) and using those methods above find the area you need.
For example you have a box with two corners out, but don't know which sides the circle cross. You can test a side by this:
- Get a side (let's call it AB). If it's horizontal you know its
y= yA=yB
coordinate. - Find the
x
coord in circle for thisy
(two solutions, +x,-x) - Test
xA < x xB
. If both solutions fail then the circle doesn't cross this side.
If the side is vertical then use its y
coord to find the x
one.
If a side has both corners out, then you can avoid testing it.
An easy approximation
Very similar to Montecarlo method
For those cells that are crossing the circuference (some but not all corners are outside) you can sub-divide each one into a n x n
grid, and test every sub-cell.
The test can be the distance from the center of the subcell (e.g. average point of its diagonal) to the center of the circle.
You assign area=0 if dist > R
and area=1 otherwise.
Then the "assigned" area of the cell is the mean; this is, the summatory of all assigned areas of the n^2 subcells, divided by the area of the cell (which is nxn).
In the worst case there are about n
subcells with wrong assigned area. This is n/n^2 = 1/n
error. So if you want to calculate with an error less than 1% you need a 100x100 grid for the grid in a cell.
Answered By - Ripi2
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.