🦀 Rust + WASM 实战系列 第 24 篇 阅读时间:约 5 分钟 | 实战可运行
📌 写在前面
任务 23 讲了怎么采 高斯和泊松噪声------这一篇讲第三种噪声(椒盐) + 一个为什么高斯无处不在的原理(中心极限定理)。
| 内容 | 物理场景 | 算法 |
|---|---|---|
| 椒盐噪声 | 老电视雪花、损坏照片 | 概率置 0/255 |
| 中心极限定理 | 解释"为什么高斯无处不在" | 多个均匀之和 → 钟形 |
🚀 TL;DR
核心概念:
-
椒盐噪声 = 每个像素
density的概率被随机设为 0(黑)或 255(白) -
CLT = 多个独立同分布随机变量之和 → 趋向正态分布(无论单个是什么分布)
1 个均匀 → 矩形分布
2 个之和 → 三角形
4 个之和 → 鼓包
12 个之和 → 几乎完美的正态
📖 目录
- 椒盐噪声:最简单也最暴力
- [3 种噪声对比(高斯 / 泊松 / 椒盐)](#3 种噪声对比(高斯 / 泊松 / 椒盐) "#%E4%BA%8C3-%E7%A7%8D%E5%99%AA%E5%A3%B0%E5%AF%B9%E6%AF%94%E9%AB%98%E6%96%AF--%E6%B3%8A%E6%9D%BE--%E6%A4%92%E7%9B%90")
- 中心极限定理:为什么高斯无处不在
- [CLT 可视化实现](#CLT 可视化实现 "#%E5%9B%9Bclt-%E5%8F%AF%E8%A7%86%E5%8C%96%E5%AE%9E%E7%8E%B0")
- 关键代码
- 前端效果展示
- 踩坑提醒
- 接下来
一、椒盐噪声:最简单也最暴力
算法(伪代码):
csharp
for each pixel:
if random() < density:
if random() < 0.5:
pixel = 0 // 黑点
else:
pixel = 255 // 白点
density:噪声像素占比(0~1)- 黑点白点各 50%------所以叫"椒盐"
Rust 代码:
rust
#[wasm_bindgen]
pub fn salt_pepper_noise(pixels: &[u8], w: u32, h: u32, density: f32) -> Vec<u8> {
for i in 0..n {
let r = uniform_random();
if r < density {
// 随机设黑或白
if (x & 1) == 0 {
out[idx] = 0;
// ...
} else {
out[idx] = 255;
// ...
}
}
}
}
二、3 种噪声对比(高斯 / 泊松 / 椒盐)
| 噪声 | 物理模型 | 算法 | 视觉 | 真实场景 |
|---|---|---|---|---|
| 高斯 | N(0,σ2) | Box-Muller | 平滑小波动 | 传感器热噪声 |
| 泊松 | Poisson(λ) | Knuth | 颗粒感(暗处明显) | 暗光摄影、医学影像 |
| 椒盐 | 概率置 0/255 | 随机数 | 黑白点散布 | 老电视、数据错误 |
三、中心极限定理:为什么高斯无处不在
定理(简化版):
多个独立的随机变量之和 → 当数量足够大时 ,趋向正态分布。
不管单个变量是什么分布!
直觉例子
| n 个均匀分布之和 | 形状 |
|---|---|
| 1 | 矩形 |
| 2 | 三角形 |
| 3 | 鼓包 |
| 4 | 接近钟形 |
| 8 | 几乎正态 |
| 12 | 完全正态 |
关键 :每个均匀都是平的,但 12 个加起来 → 漂亮的钟形。
现实世界
几乎所有"复杂现象"都符合 CLT:
- 测量误差 = 仪器误差 + 读数误差 + 温度漂移 + ...(多个独立随机)
- 人类身高 = 基因 + 营养 + 运动 + 睡眠 + ...(多个独立因素)
- 股票价格变化 = 无数交易者的买卖决策之和
- 图像噪声 = 多个电路/光学噪声源叠加
→ 都近似正态 → 高斯无处不在
四、CLT 可视化实现
CLT 不需要 WASM------用 Math.random() 算就行,更简单更快:
javascript
function computeHistogram(n) {
// n = 几个均匀分布之和
const N_SAMPLES = 10000
const N_BINS = 40
const bins = new Array(N_BINS).fill(0)
for (let i = 0; i < N_SAMPLES; i++) {
let sum = 0
for (let j = 0; j < n; j++) {
sum += Math.random() // 0~1
}
// sum ∈ [0, n],归一化到 bin
const bin = Math.floor(sum / n * N_BINS)
bins[bin]++
}
return bins
}
滑块 n=1~12------读者拖动看柱状图从"矩形"变成"钟形"。
五、关键代码
rust
#[wasm_bindgen]
pub fn salt_pepper_noise(pixels: &[u8], width: u32, height: u32, density: f32) -> Vec<u8> {
let mut out = pixels.to_vec();
let n = (width * height) as usize;
let d = density.clamp(0.0, 1.0) as f64;
for i in 0..n {
// xorshift64 PRNG(同任务 23 的实现)
let r = (xorshift_next() >> 11) as f64 / (1u64 << 53) as f64;
if r < d {
let idx = i * 4;
if /* random bit */ {
out[idx] = 0; out[idx+1] = 0; out[idx+2] = 0; // 黑
} else {
out[idx] = 255; out[idx+1] = 255; out[idx+2] = 255; // 白
}
}
}
out
}
核心 5 行------和第 23 节一样用自实现 PRNG。
六、前端效果展示
七、踩坑提醒
1. 椒盐 density 不能超过 0.5
rust
let d = density.clamp(0.0, 1.0) as f64;
50% 时图基本全黑/全白------没有意义。前端限制 0~0.5。
2. 椒盐 = 模拟"传输错误",不是"自然噪声"
| 用途 | 适合 |
|---|---|
| 模拟老电视、坏照片 | ✅ 椒盐 |
| 模拟真实相机噪声 | ❌ 用泊松 |
| 模拟电子电路噪声 | ❌ 用高斯 |
不要混用------它们是不同的物理模型。
3. CLT 需要 n > 30 才严格成立
我们画 n=1~12 主要是直觉演示------严格数学上 n ≥ 30 才"近似"。
八、接下来
任务 25:图像增强(自适应阈值 + 直方图均衡化)
任务 24 讲怎么加噪(破坏) 任务 25 讲怎么还原(修复)
两个工具:
- 自适应阈值:处理光照不均
- 直方图均衡化:让暗图变清晰
任务 26:概率驱动图像生成(随机游走 + 马尔可夫链 + 图像合成 + 修复)
一句话总结
椒盐噪声 = 概率置 0/255(最简单、最暴力)。
CLT = 多个独立随机变量之和 → 正态分布。
滑块 1~12 看柱状图从"平"变"钟"------为什么高斯无处不在的直觉。
📦 项目地址 :pixel-math-wasm 🦀 Rust + WebAssembly 实战系列
🏷️ 标签 :#Rust #WebAssembly #图像处理 #椒盐噪声 #中心极限定理 #高斯分布 #CLT

