graph TD
A[文件开始] --> B[BITMAPFILEHEADER (14 字节)]
B --> C[BITMAPINFOHEADER (40 字节)]
C --> D{是否有调色板?}
D -->|是 | E[Palette Data]
D -->|否 | F[Pixel Data]
E --> F
F --> G[填充字节(按 4 字节对齐)]
graph LR
A[输入 JPEG 流] --> B[熵解码/Huffman]
B --> C[反 Zig-Zag 重排]
C --> D[反量化 × Q-table]
D --> E[反 DCT (IDCT)]
E --> F[上采样 Cb/Cr]
F --> G[YCbCr → RGB]
G --> H[输出像素矩阵]
graph TD
A[调用 img(x,y)] --> B{是否启用边界检查?}
B -- 是 --> C[执行 if 判断]
C --> D[抛出异常或继续]
B -- 否 --> E[直接计算 offset 并返回]
style B fill:#f9f,stroke:#333
style E fill:#bbf,stroke:#fff,color:#fff
std::array<RGB, 360*101*101> hsv_to_rgb_lut;
voidbuild_hsv_lut(){
for (int h = 0; h < 360; ++h)
for (int s = 0; s <= 100; ++s)
for (int v = 0; v <= 100; ++v) {
auto rgb = hsv2rgb(h, s/100.f, v/100.f);
hsv_to_rgb_lut[h*101*101 + s*101 + v] = rgb;
}
}
查询速度提升 5 倍以上!
几何变换引擎:不只是拉伸旋转那么简单
几何变换的核心是逆向映射:遍历输出图像的每个像素,找出它在原图中的来源坐标,再插值得到颜色。
为什么要逆向?因为正向可能导致空洞或重叠。
插值策略的选择艺术
方法
质量
性能
适用场景
最近邻
差
⚡ 极快
实时系统、掩码图
双线性
中
快
Web 展示、UI 渲染
双三次
优
较慢
医疗影像、出版印刷
双线性示例:
Color bilinear_interpolate(const ximage& src, float x, float y){
int x0 = floor(x), y0 = floor(y);
int x1 = x0 + 1, y1 = y0 + 1;
float u = x - x0, v = y - y0;
float ru = 1 - u, rv = 1 - v;
Color c00 = src(x0, y0), c10 = src(x1, y0), c01 = src(x0, y1), c11 = src(x1, y1);
return ru*rv*c00 + u*rv*c10 + ru*v*c01 + u*v*c11;
}
配合 OpenMP 并行化,四核 CPU 上处理 1080p 图像可提速近 4 倍!
#pragma omp parallel forfor (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
dst(x, y) = bilinear(src, xf[x], yf[y]);
}
}
矩阵驱动变形:当图像遇上线性代数
真正的高手,都用矩阵说话。
齐次坐标:让平移也能矩阵乘
普通二维点 $(x, y)$ 加一维变成 $(x, y, 1)$,就能把仿射变换统一为矩阵乘法:
$$
\begin{bmatrix}
x' \ y' \ 1
\end{bmatrix}
\begin{bmatrix}
a & b & t_x \
c & d & t_y \
0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
x \ y \ 1
\end{bmatrix}
$$
从此,缩放、旋转、平移都可以拼接成一条链:
TransformMatrix M = TransformMatrix::translate(cx, cy)
.multiply(TransformMatrix::rotate(theta))
.multiply(TransformMatrix::translate(-cx, -cy));
绕任意点旋转?不过是'平移到原点→旋转→平回'三步曲罢了。
变换顺序 matters!
先旋转再平移 ≠ 先平移再旋转:
序列
效果
T * R
绕自身中心转完再移动
R * T
以原点为中心画圆弧
💡 记住口诀:'从右往左读,动作依次发生'
实战案例:交互式图像变形编辑器原型
结合以上技术,我们可以快速搭建一个支持拖拽控制点的 GUI 原型:
TransformMatrix solve_homography(const Point2f src[4], const Point2f dst[4]){
Eigen::Matrix<double, 8, 9> A;
for (int i = 0; i < 4; ++i) {
double x = src[i].x, y = src[i].y;
double u = dst[i].x, v = dst[i].y;
A.row(2*i) << 0, 0, 0, -x, -y, -1, v*x, v*y, v;
A.row(2*i + 1) << x, y, 1, 0, 0, 0, -u*x, -u*y, -u;
}
Eigen::JacobiSVD<Eigen::Matrix<double,8,9>> svd(A);
Eigen::Vector9d h = svd.matrixV().col(8);
returnmatrix_from_vector(h);
}