RGB 色彩矩阵变换:矩阵 × 向量 = 像素变换
🦀 Rust + WASM 实战系列 第 19 篇 阅读时间:约 5 分钟 | 实战可运行
📌 写在前面
Part 1 任务 5 讲了 7 种"单色滤镜"------sepia、灰度、暖色调......每个都是手写 RGB 系数:
rust
r = 0.393*r_old + 0.769*g_old + 0.189*b_old
g = 0.349*r_old + 0.686*g_old + 0.168*b_old
b = 0.272*r_old + 0.534*g_old + 0.131*b_old
7 种滤镜 = 7 套系数 = 7 个不同公式------重复、难记。
这一篇用"矩阵 × 向量"统一 ------1 个公式搞定所有 7 种滤镜(和 Part 1 任务 5 完全相同的效果)。
🚀 TL;DR
RGB 像素 = 3 维向量 (R,G,B),变换矩阵 = 3×3 系数表,新像素 = 矩阵 × 向量:
R′G′B′ = adgbehcfi RGB
核心代码 5 行:
rust
let m = Matrix3::new(a, b, c, d, e, f, g, h, i);
let rgb = Vector3::new(r, g, b);
let new_rgb = m * rgb;
(计算使用 nalgebra 库
📖 目录
- [前置基础:矩阵 × 向量](#前置基础:矩阵 × 向量 "#%E4%B8%80%E5%89%8D%E7%BD%AE%E5%9F%BA%E7%A1%80%E7%9F%A9%E9%98%B5--%E5%90%91%E9%87%8F")
- [RGB 像素 = 3 维向量](#RGB 像素 = 3 维向量 "#%E4%BA%8Crgb-%E5%83%8F%E7%B4%A0--3-%E7%BB%B4%E5%90%91%E9%87%8F")
- [变换矩阵 = 一张 3×3 系数表](#变换矩阵 = 一张 3×3 系数表 "#%E4%B8%89%E5%8F%98%E6%8D%A2%E7%9F%A9%E9%98%B5--%E4%B8%80%E5%BC%A0-33-%E7%B3%BB%E6%95%B0%E8%A1%A8")
- [4 个例子(亮度/对比/灰度/色调)](#4 个例子(亮度/对比/灰度/色调) "#4-%E4%B8%AA%E4%BE%8B%E5%AD%90")
- 关键代码
- 前端效果展示
- 踩坑提醒
- 接下来
一、前置基础:矩阵 × 向量(可跳过)
下面只讲一个运算 ------如果你记得,可以直接跳到 §二。
矩阵 = 数字表格
M= 0.50000.50000.5
一张 3×3 的数表。没什么神秘的。
向量 = 一列数
v= 200150100
RGB 三通道就是一个 3 维向量。
矩阵 × 向量(核心)
规则 :矩阵的每一行 和向量的每个元素对应相乘,再加起来:
0.50000.50000.5 200150100 = 0.5×2000.5×1500.5×100 = 1007550
就这么简单 :一行算一个值,3 行 = 3 个新值。
二、RGB 像素 = 3 维向量
每个像素的 RGB 三通道 = 一个 3 维向量:
pixel= RGB = 200150100
三、变换矩阵 = 一张 3×3 系数表
M= m00m10m20m01m11m21m02m12m22
9 个数字按行优先(row-major)排列:
m00 m01 m02
m10 m11 m12
m20 m21 m22
矩阵的每一行 = 怎么算新通道:
- 第 1 行 ( R′)= 怎么算新红色
- 第 2 行 ( G′)= 怎么算新绿色
- 第 3 行 ( B′)= 怎么算新蓝色
每一行有 3 个系数 a,b,c,这 3 个系数告诉"用多少原 R、多少原 G、多少原 B 混合"。
4、4 个例子
每张表 = 一个预设矩阵。
例 1:单位矩阵(不变)
I= 100010001
新像素 = 旧像素。什么都不做。
例 2:对比度增强(对角线 ×1.5)
S= 1.50001.50001.5
每个通道 ×1.5 → 颜色更鲜艳(注意:超过 255 会被截断)。
例 3:转灰度(BT.601 加权)
G= 0.2990.2990.2990.5870.5870.5870.1140.1140.114
注意三行都一样------把 RGB 按 BT.601 系数混合成灰度。
| R 系数 | G 系数 | B 系数 | 物理含义 |
|---|---|---|---|
| 0.299 | 0.587 | 0.114 | 人眼对绿色最敏感(绿色权重最大) |
例 4:怀旧(Sepia)
sepia= 0.3930.3490.2720.7690.6860.5340.1890.1680.131
经典的"老照片"色调。
5、关键代码
rust
use nalgebra::{Matrix3, Vector3};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn color_matrix(
pixels: &[u8], width: u32, height: u32,
m00: f32, m01: f32, m02: f32,
m10: f32, m11: f32, m12: f32,
m20: f32, m21: f32, m22: f32,
) -> Vec<u8> {
let w = width as usize;
let h = height as usize;
let matrix = Matrix3::new(
m00, m01, m02,
m10, m11, m12,
m20, m21, m22,
);
let mut out = vec![0u8; pixels.len()];
for i in 0..(w * h) {
let idx = i * 4;
let rgb = Vector3::new(
pixels[idx] as f32,
pixels[idx + 1] as f32,
pixels[idx + 2] as f32,
);
let new_rgb = matrix * rgb;
out[idx] = new_rgb[0].clamp(0.0, 255.0) as u8;
out[idx + 1] = new_rgb[1].clamp(0.0, 255.0) as u8;
out[idx + 2] = new_rgb[2].clamp(0.0, 255.0) as u8;
out[idx + 3] = pixels[idx + 3]; // alpha 不变
}
out
}
核心循环 5 行 ------matrix * rgb 调库,其他都是装框。
对比:手写 7 种滤镜(任务 5)和 1 个矩阵
任务 5(7 个手写函数):
rust
fn sepia(p: &[u8]) -> Vec<u8> {
// 9 行手写 RGB 系数
}
fn cool(p: &[u8]) -> Vec<u8> { /* 9 行 */ }
fn warm(p: &[u8]) -> Vec<u8> { /* 9 行 */ }
fn red_boost(p: &[u8]) -> Vec<u8> { /* 9 行 */ }
// ... 共 7 个函数 × 9 行 = 63 行
任务 19(1 个矩阵函数):
rust
fn color_matrix(p: &[u8], ..., m00..m22) -> Vec<u8> {
// 5 行循环 + 调库
}
// 1 个函数 × 5 行 = 5 行
代码量减少 12 倍------这就是矩阵抽象的力量。
6、前端效果展示
打开页面后:
- 默认显示单位矩阵(图像不变)
- 点击 4 个预设按钮:原始 / 对比度增强 / 灰度 / 怀旧
- 9 个数字输入框可以自定义任意矩阵------拖滑块看实时效果
7、踩坑提醒
1. 矩阵超过 0, 255 会被截断
rust
// 对比度 ×1.5:原本 R=200 → 300 → clamp 到 255(截断)
// 高亮区域会"爆白"------这是矩阵变换的固有问题
解决 :如果不想截断,可以先做浮点运算再一起转 u8(代码里已经做了)。
2. 矩阵不一定是"颜色"变换
RGB 矩阵可以做任何线性变换:
- 转灰度
- 反色(对角线 = -1)
- 通道交换(红绿互换)
不限于"调色"------读者可以试试输入奇怪的矩阵看效果。
3. Alpha 通道不参与变换
rust
out[idx + 3] = pixels[idx + 3]; // alpha 直接复制
透明度不在 RGB 颜色空间内,不能被 3×3 矩阵变换。
8、接下来
任务 20:PCA 主成分分析------用协方差矩阵找出图片"最关键的方向",实现图片压缩。
Mandelbrot:每个像素一个 c,z 从 0 开始 PCA:每张图片一个协方差矩阵,找最大方差方向
把 10000 维的图片压缩到 50 维的"主成分"------视觉上还能看清原图。
任务 21:最小二乘回归------用矩阵求逆解"拟合"问题,做图像去噪。
任务 22:协方差矩阵手写------深挖 PCA 内部原理,库 vs 手算对比。
一句话总结
RGB 像素 = 3 维向量,3×3 矩阵 × 向量 = 新颜色。
7 种滤镜的 7 个手写公式 → 1 个矩阵函数(5 行循环 + 调库)。
代码量减少 12 倍,这就是线性代数的"抽象力量"。
📦 项目地址 :pixel-math-wasm 🦀 Rust + WebAssembly 实战系列
🏷️ 标签 :#Rust #WebAssembly #图像处理 #矩阵 #线性代数 #RGB变换 #nalgebra