Rust图像处理第19节-RGB 色彩矩阵变换:矩阵 × 向量 = 像素变换

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)(R, G, B) (R,G,B),变换矩阵 = 3×3 系数表,新像素 = 矩阵 × 向量
R′ G′ B′ = a b c d e f g h i R G B \begin{bmatrix} R' \\ G' \\ B' \end{bmatrix} = \begin{bmatrix} a & b & c \\ d & e & f \\ g & h & i \end{bmatrix} \begin{bmatrix} R \\ G \\ B \end{bmatrix} 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


📖 目录

  1. [前置基础:矩阵 × 向量](#前置基础:矩阵 × 向量 "#%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")
  2. [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×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 个例子(亮度/对比/灰度/色调) "#4-%E4%B8%AA%E4%BE%8B%E5%AD%90")
  5. 关键代码
  6. 前端效果展示
  7. 踩坑提醒
  8. 接下来

一、前置基础:矩阵 × 向量(可跳过)

下面只讲一个运算 ------如果你记得,可以直接跳到 §二

矩阵 = 数字表格

M= 0.5 0 0 0 0.5 0 0 0 0.5 M = \begin{bmatrix} 0.5 & 0 & 0 \\ 0 & 0.5 & 0 \\ 0 & 0 & 0.5 \end{bmatrix} M= 0.50000.50000.5

一张 3×3 的数表。没什么神秘的

向量 = 一列数

v= 200 150 100 v = \begin{bmatrix} 200 \\ 150 \\ 100 \end{bmatrix} v= 200150100

RGB 三通道就是一个 3 维向量。

矩阵 × 向量(核心)

规则 :矩阵的每一行 和向量的每个元素对应相乘,再加起来:
0.5 0 0 0 0.5 0 0 0 0.5 200 150 100 = 0.5×200 0.5×150 0.5×100 = 100 75 50 \begin{bmatrix} 0.5 & 0 & 0 \\ 0 & 0.5 & 0 \\ 0 & 0 & 0.5 \end{bmatrix} \begin{bmatrix} 200 \\ 150 \\ 100 \end{bmatrix} = \begin{bmatrix} 0.5 \times 200 \\ 0.5 \times 150 \\ 0.5 \times 100 \end{bmatrix} = \begin{bmatrix} 100 \\ 75 \\ 50 \end{bmatrix} 0.50000.50000.5 200150100 = 0.5×2000.5×1500.5×100 = 1007550

就这么简单 :一行算一个值,3 行 = 3 个新值


二、RGB 像素 = 3 维向量

每个像素的 RGB 三通道 = 一个 3 维向量:
pixel= R G B = 200 150 100 \text{pixel} = \begin{bmatrix} R \\ G \\ B \end{bmatrix} = \begin{bmatrix} 200 \\ 150 \\ 100 \end{bmatrix} pixel= RGB = 200150100


三、变换矩阵 = 一张 3×3 系数表

M= m00 m01 m02 m10 m11 m12 m20 m21 m22 M = \begin{bmatrix} m_{00} & m_{01} & m_{02} \\ m_{10} & m_{11} & m_{12} \\ m_{20} & m_{21} & m_{22} \end{bmatrix} M= m00m10m20m01m11m21m02m12m22

9 个数字按行优先(row-major)排列:

复制代码
m00 m01 m02
m10 m11 m12
m20 m21 m22

矩阵的每一行 = 怎么算新通道

  • 第 1 行 R′R' R′)= 怎么算新红色
  • 第 2 行 G′G' G′)= 怎么算新绿色
  • 第 3 行 B′B' B′)= 怎么算新蓝色

每一行有 3 个系数 a,b,ca, b, c a,b,c,这 3 个系数告诉"用多少原 R、多少原 G、多少原 B 混合"


4、4 个例子

每张表 = 一个预设矩阵

例 1:单位矩阵(不变)

I= 1 0 0 0 1 0 0 0 1 I = \begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \end{bmatrix} I= 100010001

新像素 = 旧像素。什么都不做

例 2:对比度增强(对角线 ×1.5)

S= 1.5 0 0 0 1.5 0 0 0 1.5 S = \begin{bmatrix} 1.5 & 0 & 0 \\ 0 & 1.5 & 0 \\ 0 & 0 & 1.5 \end{bmatrix} S= 1.50001.50001.5

每个通道 ×1.5 → 颜色更鲜艳(注意:超过 255 会被截断)。

例 3:转灰度(BT.601 加权)

G= 0.299 0.587 0.114 0.299 0.587 0.114 0.299 0.587 0.114 G = \begin{bmatrix} 0.299 & 0.587 & 0.114 \\ 0.299 & 0.587 & 0.114 \\ 0.299 & 0.587 & 0.114 \end{bmatrix} 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.393 0.769 0.189 0.349 0.686 0.168 0.272 0.534 0.131 \text{sepia} = \begin{bmatrix} 0.393 & 0.769 & 0.189 \\ 0.349 & 0.686 & 0.168 \\ 0.272 & 0.534 & 0.131 \end{bmatrix} 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、前端效果展示

打开页面后:

  1. 默认显示单位矩阵(图像不变)
  2. 点击 4 个预设按钮:原始 / 对比度增强 / 灰度 / 怀旧
  3. 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

相关推荐
white_ant4 小时前
15-ASCII 转换工具使用指南
rust·tauri·watools
铅笔侠_小龙虾7 小时前
Rust 学习(2)-变量、常量与 shadowing
学习·算法·rust
独孤留白11 小时前
从C到Rust:Mutex / RwLock / Atomic 跨线程局部可变性
rust
SmalBox11 小时前
【节点】[EvaluateScatteringColor节点]原理解析与实际应用
unity3d·游戏开发·图形学
程序员爱钓鱼13 小时前
Rust 注释与文档注释详解:从代码说明到 cargo doc
后端·rust
花褪残红青杏小1 天前
Rust图像处理第18节-分形画布缩放 + 拖拽交互:图像缩放 + f32 性能优化
rust·webassembly·图形学
doiito1 天前
【开源项目】用 Rust 重构中文 TTS!kokoroi-rs 高性能语音合成引擎介绍
ai·rust
铅笔侠_小龙虾1 天前
Rust 学习目录
开发语言·学习·rust
SmalBox1 天前
【节点】[EvaluateRefractionData节点]原理解析与实际应用
unity3d·游戏开发·图形学