【问题标题】:Render a Circle or Ellipse with Anti-Aliasing使用抗锯齿渲染圆形或椭圆
【发布时间】:2020-03-02 17:02:49
【问题描述】:

假设我有一个给定大小的方形栅格,并且我想“绘制”(渲染)一个给定半径(或长轴/短轴)和中心的圆(或椭圆)。

在 Python 中使用 NumPy 执行此操作的一种方法是:

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.]]

在没有anti-aliasing 的情况下产生光栅化。 特别是,仅部分包含在圆中的像素的值是0(类似于从圆中排除的像素),而完全包含在圆中的像素的值是@987654326 @。

使用抗锯齿功能,圆圈中部分包含的像素将获得介于01 之间的值,具体取决于圆圈中包含的区域的多少。


如何将上面的代码修改为(可能很便宜)包含抗锯齿功能? 我正在努力了解如何(如果?)我可以使用 arr 的值。

基于超级采样的方法在这里是不可能的。

最终,结果应该类似于:

# [[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]]

(其中0.2 应该是介于0.01.0 之间的值,表示该特定像素的多少区域被圆圈覆盖)。


编辑

我现在看到了如何调整来自 Creating anti-aliased circular mask efficiently 的代码的明显方法,尽管很明显,np.clip() 必须是解决方案的一部分。

【问题讨论】:

  • 不只是 return arr &lt;= 1.0 使它成为二进制吗?
  • @usr2564301 是的,但是arr 的值给出了像素中心到理想圆心的距离的一些指示。不是理想圆线内有多少像素。如果你只是返回arr,你会得到距离(平方)。而不是内部 1.0、外部 0.0 以及边界像素介于两者之间的东西,这就是抗锯齿的工作原理。
  • 这能回答你的问题吗? Creating anti-aliased circular mask efficiently
  • 您在寻找数学上的正确答案吗?将像素四等分可以获得 0.25 的准确度;除以 10 将在 0.01 秒内。超级采样既简单又准确。

标签: python numpy antialiasing


【解决方案1】:

一种快速但不一定在数学上正确的方法(大致基于Creating anti-aliased circular mask efficiently 的代码)是:

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.  ]]

raster_geometry Python 包中包含此方法的更通用版本(免责声明:我是它的主要作者)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-02
    • 2012-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多