1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
// 噪声生成(纯 MoonBit):add_noise_gaussian / add_noise_salt_pepper
// 使用简单的 LCG 伪随机数生成器,种子固定以保证可测试性
///|
/// 简单 LCG 伪随机数生成器状态
priv struct LCG {
mut state : UInt
}
///|
fn lcg_next(rng : LCG) -> UInt {
rng.state = rng.state * 1103515245U + 12345U
rng.state
}
///|
fn lcg_float(rng : LCG) -> Float {
let v = lcg_next(rng)
Float::from_int((v & 0xFFFFFFU).reinterpret_as_int()) / 16777216.0F
}
///|
/// Box-Muller 变换生成标准正态分布随机数
fn lcg_gaussian(rng : LCG) -> Float {
let u1 = lcg_float(rng)
let u2 = lcg_float(rng)
let safe_u1 = if u1 < 0.0001F { 0.0001F } else { u1 }
// ln(x) = log2(x) * ln(2), sqrt(x) = powf(x, 0.5)
let ln_u1 = @math.log2(safe_u1.to_double()) * 0.6931471805599453
let r = Float::from_double(ln_u1 * -2.0).sqrt()
r * @math.cosf(2.0F * 3.14159265F * u2)
}
///|
///|
/// 添加高斯噪声。sigma 为标准差,seed 为随机种子。
/// 仅影响颜色通道,alpha 通道不变。
pub fn add_noise_gaussian(
img : @types.Image,
sigma : Float,
seed : UInt,
) -> @types.Image {
let rng = LCG::{ state: seed }
let n = img.width * img.height
let ch = img.channels
let color_ch = if ch >= 3 { 3 } else { ch }
let out : Array[Byte] = Array::make(n * ch, b'\x00')
for i in 0..<n {
for c in 0..<color_ch {
let noise = lcg_gaussian(rng) * sigma
let v = img.data[i * ch + c].to_int() + noise.to_int()
out[i * ch + c] = clamp_b(v)
}
if ch == 4 {
out[i * ch + 3] = img.data[i * ch + 3]
}
}
@types.Image::{
width: img.width,
height: img.height,
channels: ch,
data: Bytes::from_array(out),
}
}
///|
/// 添加椒盐噪声。prob 为噪声概率(0.0~1.0),seed 为随机种子。
/// 被选中的像素随机设为 0(椒)或 255(盐)。
pub fn add_noise_salt_pepper(
img : @types.Image,
prob : Float,
seed : UInt,
) -> @types.Image {
let rng = LCG::{ state: seed }
let n = img.width * img.height
let ch = img.channels
let color_ch = if ch >= 3 { 3 } else { ch }
let out : Array[Byte] = Array::make(n * ch, b'\x00')
for i in 0..<n {
let r = lcg_float(rng)
if r < prob {
let salt = lcg_float(rng) < 0.5F
let val = if salt { b'\xFF' } else { b'\x00' }
for c in 0..<color_ch {
out[i * ch + c] = val
}
} else {
for c in 0..<ch {
out[i * ch + c] = img.data[i * ch + c]
}
}
}
@types.Image::{
width: img.width,
height: img.height,
channels: ch,
data: Bytes::from_array(out),
}
}