基于密度的数字图像重采样方法及示例:仿古马赛克
一种非常简单的从数字图像中重采样点并将其绘制成仿古马赛克、点(点彩)和Voronoi单元的方法。
引言
在本文中,我将介绍一种新的且非常简单的从数字图像中重采样点并将其绘制成看起来像仿古马赛克、点(点彩)和Voronoi单元的方法。该方法基于计算基于密度的核大小,然后进行采样并消除由圆形核跨越的剩余邻居点。
在继续之前,我强烈建议您查看“参考文献”部分中的文章。
通常解释一种方法及其逻辑背景是从展示我们想要做什么开始
第一步是获取重采样点,第二步是使用这些采样点绘制我们想要的东西。
输入图像 | 采样点 |
![]() | ![]() |
仿古马赛克 | 点(点彩) |
![]() | ![]() |
Voronoi单元 | 点(描边) |
![]() | ![]() |
方法
从技术上讲,数字图像的重采样被称为“空间采样”。在实践中使用了许多采样方法,例如规则网格、不规则网格、六边形网格、三角形网格、泊松盘采样、随机采样、准随机采样等。其中一些采样方法与统计分布有密切关系。主要目标是在数学上和艺术风格化上获得平衡的分布(即采样点)。在这里,我将解释另一种基于密度的采样方法。对于数字图像,我们认为“密度”是图像的亮度(即像素的灰度级)。
步骤
![]() | 这是输入图像及其像素。 让我们按照以下步骤扫描图像中的所有像素。 |
![]() | 1. 将图像转换为灰度。 |
![]() | 2. 获取每个像素的灰度值。 |
![]() | 3. 基于这些灰度值(即密度)计算圆形核半径。 |
![]() | 4. 消除圆形核(窗口)中剩余的邻居像素。 |
![]() | 5. 结果,我们得到一个重采样像素和其他白色像素。 |
如果我们对每个像素重复这些步骤,我们将得到所有重采样像素。所以,第一步完成了。
之后,我们将基于这些采样像素的颜色和之前计算的相关半径值绘制一些形状。这些步骤更容易,不需要解释。
现在,请以轻松的心情再次查看上面的图像。
代码
计算核半径的公式是我自己所有权的一个计算公式。
kernelRadius := (256 div density) - (gray div density);
典型的参数值是密度为20,阈值为192。
Using the Code
用核心亮点代码开始!
type
TCDots = record
Cx, Cy: integer;
CRadius: integer;
CColor: TColor;
end;
var
dots: array of TCDots;
...
...
SetLength(dots, nSize);
...
...
for y:=0 to H-1 do
begin
for x:=0 to W-1 do
begin
c := imgWork.Canvas.Pixels[x, y];
r := GetRValue(c);
g := GetGValue(c);
b := GetBValue(c);
gray := trunc(r * 0.2989 + g * 0.5866 + b * 0.1145 + 0.5);
//////////////////////////////////////////////////////////////
// (c) Copyright AG 2022. ademgunes@yahoo.com
// Not allowed to use for commercial apps without permissions.
kernelRadius := (256 div density) - (gray div density); // copyrighted
if chkFixedDensity.Checked then
kernelRadius := 16 - (gray div 32); // trivial
//////////////////////////////////////////////////////////////
if (kernelRadius < 5) then kernelRadius := 5;
radii2 := kernelRadius * kernelRadius;
if (gray > threshold) then
begin
c1 := clWhite;
imgWork.Canvas.Pixels[x, y] := c1;
continue;
end;
// stippling
for ky:=-kernelRadius to +kernelRadius do
begin
yy := y + ky;
if (yy < 0) then yy := 0; if (yy > H-1) then yy := H-1;
for kx:=-kernelRadius to +kernelRadius do
begin
xx := x + kx;
if (xx < 0) then xx := 0; if (xx > W-1) then xx := W-1;
if ( (ky = 0) and (kx = 0) ) then
c1 := c
else
c1 := clWhite;
if (kx*kx + ky*ky < radii2) then
begin
imgWork.Canvas.Pixels[xx, yy] := c1;
if (c1 <> clWhite) then
with dots[nCount] do
begin
Cx := xx;
Cy := yy;
CRadius := kernelRadius;
CColor := c1;
end;
end; // if
end; // kx
end; // ky
...
end; // x
...
end; // y
...
注意:为了简单起见,我在这里使用了Canvas.Pixels[..]
,但是为了优化速度,请不要在实际应用中使用Canvas.Pixels[..]
。
结论和关注点
说实话,没什么可讨论的。为了避免更复杂和昂贵的方法,请跟随我。:)
参考文献
- 文章:加权Voronoi点彩
- 文章:泊松盘样本集的样本消除
- StippleGen:Windell Oskay 在 Processing 中加权 Voronoi 点彩和 TSP 路径
- Mike Bostock 的 Voronoi 点彩
- Selim Tezel 的 Voronoi:收缩圆算法
- 书籍:基于图像和视频的艺术风格化
历史
- 2022年4月7日:初始版本