使用抗锯齿渲染圆形或椭圆形
Render a Circle or Ellipse with Anti-Aliasing
假设我有一个给定大小的正方形光栅,我想"draw"(渲染)给定半径(或长轴/短轴)和中心的圆(或椭圆)。
在 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
(类似于从圆中排除的像素),而完全包含在圆中的像素得到1
.
的值
使用抗锯齿后,部分 包含在圆圈中的像素的值将介于 0
和 1
之间,具体取决于它们的面积大小已包含在圆圈中。
我如何修改上面的代码以(可能很便宜)包括抗锯齿?
我正在努力了解如何(如果?)我可以使用 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.0
和 1.0
之间的值,表示该特定像素的面积有多少被圆圈覆盖)。
编辑
我现在看到了如何调整来自 的代码的显而易见的方法,尽管显然 np.clip()
必须是解决方案的一部分。
一种快速但不一定在数学上正确的方法(大致基于 中的代码)是:
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 包中包含了此方法的一个更通用的版本(免责声明:我是它的主要作者) .
假设我有一个给定大小的正方形光栅,我想"draw"(渲染)给定半径(或长轴/短轴)和中心的圆(或椭圆)。
在 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
(类似于从圆中排除的像素),而完全包含在圆中的像素得到1
.
使用抗锯齿后,部分 包含在圆圈中的像素的值将介于 0
和 1
之间,具体取决于它们的面积大小已包含在圆圈中。
我如何修改上面的代码以(可能很便宜)包括抗锯齿?
我正在努力了解如何(如果?)我可以使用 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.0
和 1.0
之间的值,表示该特定像素的面积有多少被圆圈覆盖)。
编辑
我现在看到了如何调整来自 np.clip()
必须是解决方案的一部分。
一种快速但不一定在数学上正确的方法(大致基于
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 包中包含了此方法的一个更通用的版本(免责声明:我是它的主要作者) .