Render a Circle or Ellipse with Anti-Aliasing

大城市里の小女人 提交于 2021-01-28 05:24:19

问题


Assume I have a square raster of given size, and I want to "draw" (render) a circle (or ellipse) of given radius (or major / minor axes) and center.

One way of doing this in Python with NumPy is:

import numpy as np


def ellipse(box_size, semisizes, position=0.5, n_dim=2):
    shape = (box_size,) * n_dim
    if isinstance(semisizes, (int, float)):
        semisizes = (semisizes,) * n_dim
    position = ((box_size - 1) * position,) * n_dim
    grid = [slice(-x0, dim - x0) for x0, dim in zip(position, shape)]
    position = np.ogrid[grid]
    arr = np.zeros(shape, dtype=float)
    for x_i, semisize in zip(position, semisizes):
        arr += (np.abs(x_i / semisize) ** 2)
    return arr <= 1.0

print(ellipse(5, 2).astype(float))
# [[0. 0. 1. 0. 0.]
#  [0. 1. 1. 1. 0.]
#  [1. 1. 1. 1. 1.]
#  [0. 1. 1. 1. 0.]
#  [0. 0. 1. 0. 0.]]

which produces a rasterization without anti-aliasing. In particular, the pixels that are only partially included in the circle get a value of 0 (similarly to pixels excluded from the circle), while pixels entirely included in the circle gets a value of 1.

With anti-aliasing, the pixels partially included in the circle would get a value between 0 and 1 depending on how much of their area is included in the circle.


How could I modify the code from above to (possibly cheaply) include anti-aliasing? I am struggling to see how (if?) I could use the values of arr.

Super-sampling-based methods are out of question here.

Eventually, the result should look something like:

# [[0.0 0.2 1.0 0.2 0.0]
#  [0.2 1.0 1.0 1.0 0.2]
#  [1.0 1.0 1.0 1.0 1.0]
#  [0.2 1.0 1.0 1.0 0.2]
#  [0.0 0.2 1.0 0.2 0.0]]

(where 0.2 should be a value between 0.0 and 1.0 representing how much area of that specific pixel is covered by the circle).


EDIT

I see now obvious way on how to adapt the code from Creating anti-aliased circular mask efficiently although obviously, np.clip() must be part of the solution.


回答1:


One fast but not necessarily mathematically correct way of doing this (loosely based on the code from Creating anti-aliased circular mask efficiently) is:

import numpy as np


def prod(items, start=1):
    for item in items:
        start *= item
    return start


def ellipse(box_size, semisizes, position=0.5, n_dim=2, smoothing=1.0):
    shape = (box_size,) * n_dim
    if isinstance(semisizes, (int, float)):
        semisizes = (semisizes,) * n_dim
    position = ((box_size - 1) * position,) * n_dim
    grid = [slice(-x0, dim - x0) for x0, dim in zip(position, shape)]
    position = np.ogrid[grid]
    arr = np.zeros(shape, dtype=float)
    for x_i, semisize in zip(position, semisizes):
        arr += (np.abs(x_i / semisize) ** 2)
    if smoothing:
        k = prod(semisizes) ** (0.5 / n_dim / smoothing)
        return 1.0 - np.clip(arr - 1.0, 0.0, 1.0 / k) * k
    elif isinstance(smoothing, float):
        return (arr <= 1.0).astype(float)
    else:
        return arr <= 1.0


n = 1
print(np.round(ellipse(5 * n, 2 * n, smoothing=0.0), 2))
# [[0. 0. 1. 0. 0.]
#  [0. 1. 1. 1. 0.]
#  [1. 1. 1. 1. 1.]
#  [0. 1. 1. 1. 0.]
#  [0. 0. 1. 0. 0.]]

n = 1
print(np.round(ellipse(5 * n, 2 * n, smoothing=1.0), 2))
# [[0.   0.65 1.   0.65 0.  ]
#  [0.65 1.   1.   1.   0.65]
#  [1.   1.   1.   1.   1.  ]
#  [0.65 1.   1.   1.   0.65]
#  [0.   0.65 1.   0.65 0.  ]]

A slightly more general version of this approach has been included in the raster_geometry Python package (Disclaimer: I am the main author of it).



来源:https://stackoverflow.com/questions/60493488/render-a-circle-or-ellipse-with-anti-aliasing

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!