Engee 文档
Notebook

巴恩斯利蕨

考虑了众所周知的分形算法"Barnsley'S Fern"的实现,该算法使用迭代函数系统生成蕨类植物的图像。

导言

Barnsley Fern算法是使用迭代函数系统(Ifs)生成分形的一个例子。 它由数学家Michael Barnsley提出,允许您通过应用具有一定概率的简单仿射变换来创建蕨叶的逼真图像。

通过对平面上的点重复应用随机仿射变换来构造分形。 四个变换中的每一个都对应于植物的特定部分(茎,左分支,右分支和尖端),并且选择这些变换的概率决定了蕨类植物的特征外观。

该算法由于其清晰度和数学美感而被广泛用作分形理论和计算机图形学中的教育示例。

In [ ]:
# 安装所需的软件包
import Pkg; Pkg.add("Images")
In [ ]:
using Images

主要部分

定义结构 BarnsleyFern

mutable struct 定义一个可变的数据结构,其中存储所有参数和分形的当前状态。

In [ ]:
mutable struct BarnsleyFern
    # 图像的大小以像素为单位的宽度和高度
    width::Int
    height::Int
    # 蕨类植物将被绘制的颜色
    color::RGB
    # 点的当前坐标为x和y
    x::Float64
    y::Float64
    # 图像像素的二维阵列(矩阵)
    fern::Matrix{RGB}

    # 结构构造函数。 创建新的BarnsleyFern()对象时调用
    function BarnsleyFern(width, height, color = RGB(0.0, 1.0, 0.0), bgcolor = RGB(0.0, 0.0, 0.0))
        # 创建一个大小宽高的空图像,
        # 用背景颜色bgcolor填充它
        img = [bgcolor for x in 1:width, y in 1:height]
        
        # 以像素为单位计算中心点的初始坐标:
        # 计算cx时考虑到x值的范围从-2.182到2.6558(约4.8378)
        cx = Int(floor(2.182 * (width - 1) / 4.8378) + 1)
        # cy的计算考虑了y值从0到9.9983的范围
        cy = Int(floor(9.9983 * (height - 1) / 9.9983) + 1)
        
        # 在起始点设置第一个像素
        img[cx, cy] = color
        
        # 创建BarnsleyFern结构的新实例
        # 具有指定的参数和初始化的图像矩阵
        return new(width, height, color, 0.0, 0.0, img)
    end
end

结构 BarnsleyFern 它包含有关图像的大小,点的当前位置,渲染的颜色以及像素矩阵本身的信息。 构造函数创建一个黑色背景矩形,并在绘图区域的下部中心区域设置蕨类植物生长的起点。

定义点变换

执行算法一次迭代的函数:选择四个仿射变换中的一个并将其应用于点的当前坐标 (f.x, f.y),然后将新的真实坐标转换为离散像素坐标,并在图像中填充相应的像素。

In [ ]:
function transform(f::BarnsleyFern)
    # 生成从0到99(含)的随机数
    r = rand(0:99)
    
    # 根据r的值,选择4个仿射变换中的一个。:
    # 选择概率是为了让你对蕨类植物有一个现实的看法。:
    # 1%—垂直压缩(主干)
    # 85%-顶部的大叶子(主要转化)
    # 7%-左边的小叶子
    # 7%-右边的小叶子
    
    if r < 1
        # 把一个点变成树干的根
        f.x = 0.0
        f.y = 0.16 * f.y
    elseif r < 86
        # 主要转变:叶的主要质量的形成
        f.x = 0.85 * f.x + 0.04 * f.y
        f.y = -0.04 * f.x + 0.85 * f.y + 1.6
    elseif r < 93
        # 叶子左侧的形成
        f.x = 0.2 * f.x - 0.26 * f.y
        f.y = 0.23 * f.x + 0.22 * f.y + 1.6
    else
        # 叶子右侧的形成
        f.x = -0.15 * f.x + 0.28 * f.y
        f.y = 0.26 * f.x + 0.24 * f.y + 0.44
    end

    # ########################################################################
    # 从笛卡尔坐标到图像矩阵索引的转换
    # 由于取值范围为(x∈[-2.182,2.6558],y∈[0,9.9983])
    # 您需要正确缩放坐标并将其转换为像素数。

    # 对于x坐标:
    # x=-2.182的最小值必须对应于第一列(索引1)
    # 最大值x=2.6558→宽度-1
    cx = Int(floor((f.x + 2.182) * (f.width - 1) / 4.8378) + 1)

    # 为y坐标:
    # 在屏幕的坐标系中,Y轴指向下方,因此有必要
    # 执行坐标变换(相对于水平轴的反射)
    # y=0→最后一行高度
    # y=9.9983→第一排1
    cy = Int(floor((9.9983 - f.y) * (f.height - 1) / 9.9983) + 1)

    # 我们在安装像素之前检查图像的边界
    if 1 <= cx <= f.width && 1 <= cy <= f.height
        # 将新坐标处的像素设置为指定的颜色
        f.fern[cx, cy] = f.color
    end
end
Out[0]:
transform (generic function with 1 method)

功能 transform() 模拟随机IFS过程。 在每一步,她随机选择四个仿射变换中的一个,改变当前点的坐标,并将这个点写在图像上。

从函数定义区域移动时保持比例()到像素矩阵,使用缩放因子和移位:

-对于X: cx = floor((x + 2.182) × (ширина − 1) ÷ 4.8378)
-为Y: cy = floor((9.9983 − y) × (высота − 1) ÷ 9.9983) 镜像发生的地方

执行迭代并显示结果

创建我们的500×500像素分形的实例

In [ ]:
const fern = BarnsleyFern(500, 500)
WARNING: redefinition of constant Main.fern. This may fail, cause incorrect answers, or produce other errors.
Out[0]:
BarnsleyFern(500, 500, RGB{Float64}(0.0,1.0,0.0), 0.0, 0.0, RGB[RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0) … RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0); RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0) … RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0); … ; RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0) … RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0); RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0) … RGB{Float64}(0.0,0.0,0.0) RGB{Float64}(0.0,0.0,0.0)])

我们运行了一百万次迭代–每个步骤应用其中一个仿射变换,并将一个点添加到图像的相应像素。

In [ ]:
for _ in 1:1000000
    transform(fern)
end

我们将结果作为像素的转置矩阵发送回来。 这是由于屏幕和数学坐标系中轴的方向不同造成的。

In [ ]:
fern.fern'
Out[0]:
No description has been provided for this image

该代码块开始分形图像的生成。 它需要一百万个迭代步骤,然后我们转向字段的内容。 .fern 我们转置矩阵,以便在图形库中正确显示。

结论

我们已经详细分析了Barnsley Fern算法在Julia编程语言中的实现。 它演示了可迭代函数系统(IFS)的使用,并允许您直观地看到自相似对象的分形性质。

该代码以RGB值矩阵的形式创建彩色图像,可以以各种方式进一步使用或可视化。

这种方法对于教育目的,理解分形的原理,以及在使用递归图形对象和建模自然结构时的实际编程非常有用。

该示例是使用[Rosetta代码]的材料开发的(https://rosettacode.org/wiki/Barnsley_fern