
简介一份基于Matlab主成分分析PCA的图像压缩与重建实现资料面向计算机、电子信息工程、数学等专业学生适用于课程设计、期末大作业或毕业设计中的算法验证与代码参考。资源包含5个.m源码文件、2个txt说明文档和1张png示例图片共8个文件压缩包仅126KB结构紧凑便于下载与快速部署。源码围绕PCA去相关、主成分特征图像提取、图像压缩与按需重建展开配有技术说明和说明文档可帮助读者理解如何将图像信息浓缩到少数主成分中并依据主成分内容恢复不同层次质量的图像。目前已有204人学习下载。通过对照源码和文档读者可掌握PCA图像压缩的基本流程、参数调节思路及重建效果评估方法适合需要动手实践和二次开发的入门及进阶学习者。1. PCA图像压缩的核心思路把图像当成数据矩阵来看主成分分析做图像压缩本质是把「像素矩阵」重写成「样本×特征」的数据矩阵再在这个矩阵上找方差最大的投影方向。一幅512×512灰度图有262144个值按8×8分块后变成4096个样本、每样本64维。自然图像相邻像素高度相关64维里真正有效的信息往往只有十几个维度PCA做的就是把它们找出来把其余维度丢弃再用保留的方向把图像重建回来。相比固定丢弃高频系数的阈值滤波PCA按图像自身分布自适应选方向不需要人工定频带相比JPEG它在低码率下保留更多全局结构缺点是计算量更大、分块接缝明显。它是深度学习自编码器流行之前最经典的线性降维基线很适合Matlab课程设计、图像压缩实验对比以及想验证「线性模型对图像到底能压到多少」的工程师。下文代码全部在Matlab R2018b及以上可用只依赖eig、svd和基础矩阵运算不涉及额外工具箱。2. Matlab里从零实现PCA图像压缩从分块到特征分解2.1 为什么先分块再按行展开把整幅图像直接做PCA常见做法是把每一行当作一个样本把列数当作特征数。512×512的图像会得到512个样本、512维特征协方差矩阵是512×512分解不算慢但问题在于整行像素包含的天空、建筑、纹理混在一起主成分描述的是全局行统计重建时细节和边缘会被平均掉。分块后每个8×8块只覆盖局部纹理块与块之间的统计特性更接近前几个主成分就能抓住块内的主要灰度变化。这和JPEG选8×8做DCT是同一个直觉自然图像的局部统计比全局统计稳定得多。分块尺寸还决定了样本数和特征维数的比例这个比例直接影响协方差估计的可靠性。8×8分块下dim64512×512图像对应4096个样本样本/特征比达到64协方差矩阵估计足够稳。若改用16×16分块dim变成256同样图像下样本数降到1024比例只有4特征向量开始出现过拟合重建图像会出现与训练块相关的伪纹理。所以「先分块、再按行展开」不只是为了局部性和计算开销更是为了让协方差统计站得住脚。2.2 协方差矩阵与特征分解的最小实现下面这个函数是整套压缩的核心输入灰度图像、块尺寸和保留主成分数k输出重建图像。所有中间量都保留在变量名里方便把它拆成独立的压缩端和解压端脚本。function recon pca_block_compress(img_gray, blockSize, k) [rows, cols] size(img_gray); rBlocks floor(rows / blockSize); cBlocks floor(cols / blockSize); nBlocks rBlocks * cBlocks; dim blockSize * blockSize; % 1. 分块并展平: X的每一行是一个块, 共nBlocks行 X zeros(nBlocks, dim); idx 0; for i 1:rBlocks for j 1:cBlocks r0 (i-1)*blockSize 1; c0 (j-1)*blockSize 1; block img_gray(r0:r0blockSize-1, c0:c0blockSize-1); idx idx 1; X(idx, :) block(:); end end % 2. 中心化: 每个块减去平均块, PCA必须在零均值数据上做 meanX mean(X, 1); % 1×dim 平均块 Xc X - meanX; % 中心化后的样本矩阵 % 3. 协方差矩阵并做特征分解 C (Xc * Xc) / (nBlocks - 1); % dim×dim [V, D] eig(C); [~, order] sort(diag(D), descend); % 特征值降序排列 V V(:, order); % 4. 截断到前k个主成分并投影 Vk V(:, 1:k); score Xc * Vk; % nBlocks×k, 压缩后的核心数据 % 5. 重建: 投影坐标乘回特征向量, 加回平均块 reconX score * Vk meanX; % 6. 写回图像矩阵 recon zeros(rows, cols); idx 0; for i 1:rBlocks for j 1:cBlocks idx idx 1; r0 (i-1)*blockSize 1; c0 (j-1)*blockSize 1; recon(r0:r0blockSize-1, c0:c0blockSize-1) ... reshape(reconX(idx, :), blockSize, blockSize); end end recon max(0, min(255, recon)); % 裁剪越界像素 end这段代码里最容易被忽略的是第2步中心化。meanX必须进入重建通路score乘回Vk得到的只是块间差异量没有加回平均块的图像是整体偏灰的残差图。把score、Vk、meanX一起保存成.mat或二进制文件时加载顺序必须和这里一致。第3步用eig(C)处理64×64的小矩阵完全够用但如果blockSize取16以上dim变成256协方差矩阵开始病态应改用下一节的SVD写法。提示输入图像必须转成double再送入函数。uint8在减法和矩阵乘法中会溢出典型症状是重建图像出现整块偏白或偏黑的条纹。2.3 用SVD替代特征分解的数值稳定写法eig(C)分解的对象是C Xc*Xc/(n-1)先做一次矩阵乘法会放大Xc中异常块的影响。直接对Xc做SVD右奇异向量V就是协方差矩阵的特征向量奇异值平方除以(n-1)就是特征值绕开了平方放大效应。% 用econ SVD替换2.2节第3~4步 % Xc 为中心化后的样本矩阵, nBlocks×dim [~, S, V] svd(Xc, econ); % Xc U*S*V eigVals diag(S).^2 / (nBlocks - 1); % 特征值序列 Vk V(:, 1:k); score Xc * Vk; % 投影系数与eig路径等价econ模式在nBlocks大于dim时返回dim×dim的完整V在nBlocks小于dim时返回nBlocks×nBlocks的V此时k不能超过nBlocks。另有eigs(C, k, largestabs)只算前k个特征向量适合dim上万的大分块场景但对64维小矩阵没有性能收益反而要处理收敛警告。我的默认选择是dim≤128用svd(econ)dim更大且只关心前k个主成分时用eigs。| 比较项 | eig(C) | svd(Xc,econ) | eigs(C,k) | | 计算对象 | dim×dim协方差 | nBlocks×dim原始数据 | dim×dim协方差(迭代) | | 特征值来源 | 直接分解 | 奇异值平方/(n-1) | 部分最大特征值 | | 数值稳定性 | 病态时可能出现负特征值 | 最稳, 推荐默认使用 | 依赖收敛容差 | | 适用场景 | 小dim、教学演示 | 通用首选 | dim大且k固定 |「负特征值」值得单独说图像块高度相似时协方差矩阵接近秩亏浮点误差可能把本应为0的最小特征值算成微小的负数。eig照常返回排序后这些负值落到末尾不影响前k个结果但如果你用特征值算能量占比求和时负值会拉低累计贡献率判断95%阈值前需要先对特征值做max(0,·)截断。3. 主成分数量k与分块大小的参数权衡3.1 用累计贡献率决定保留多少主成分k是压缩质量和存储量之间唯一的旋钮。选k有两条路按目标压缩比反推k或按信息保留程度正推k。后者更常用定义是特征值降序排列后前k个之和占总能量的比例。经验阈值取95%对多数自然图像8×8分块下k在10到25之间就能达到这个数。% 承接2.2节的特征值结果 eigVals max(0, diag(D)); % 截断负特征值 totalEnergy sum(eigVals); cumRatio cumsum(eigVals) / totalEnergy; k_95 find(cumRatio 0.95, 1, first); k_99 find(cumRatio 0.99, 1, first); fprintf(95%%能量需k%d, 99%%能量需k%d\n, k_95, k_99);cumsum得到每个k对应的累计占比find取第一个越过阈值的下标。注意这里的能量是训练块的方差统计量只代表块内方差的重建程度不代表视觉质量。平滑图像如天空和皮肤k8时PSNR已经很高高纹理图像如树叶、布料k20仍然能看到细节丢失。贡献率适合定初值最终还要用PSNR和肉眼微调。3.2 压缩比的计算公式与实验数据压缩比必须把模型参数算进去否则会虚高。灰度图原始存储是rows×cols字节压缩后存储包括三部分每个块的投影系数scorenBlocks×k、k个特征向量k×dim、平均块向量meanXdim。originalBytes rows * cols; compressedBytes nBlocks * k dim * k dim; % 第一项: score矩阵, 每个块k个系数 % 第二项: Vk特征向量, 每列dim维, 共k列 % 第三项: meanX平均块, dim维 compressionRatio originalBytes / compressedBytes; % score和Vk按与原像素相同的浮点精度计算, 不含量化以512×512灰度图、blockSize8为例nBlocks4096、dim64不同k的理论压缩比和典型重建质量如下。PSNR列给的是自然图像上常见的范围精确值依赖具体图片纹理。| 保留主成分数k | 压缩后存储(字节) | 压缩比 | 典型PSNR(dB) | | 4 | 4×40962566416644 | 15.8 | 23~26 | | 8 | 8×40965126433344 | 7.9 | 27~29 | | 16 | 16×409610246466624 | 3.9 | 31~33 | | 32 | 32×4096204864133184 | 2.0 | 36~38 | | 64 | 64×4096409664266304 | 0.98 | 无损 |k64时压缩比小于1因为特征向量和平均块成为额外开销。这恰好说明PCA压缩的收益完全来自块内维度的相关性相关性越强k可以越小。实际工程里score和Vk通常会量化成8位整数压缩比还能再提高但量化误差会直接体现为重建图像上的颗粒噪点量化位宽是压缩比和噪声之间的另一个旋钮。3.3 分块大小对压缩质量和边界效应的影响blockSize取4、8、16各有代价。4×4的dim16块内方差小平均k3到5就能达到95%能量但块数目变成16384score存储量反而上升且每块统计噪声大重建后容易出现颗粒感。16×16的dim256能量更集中但样本数降到1024协方差估计不稳块间不连续处会出现明显马赛克边界。8×8是压缩率和视觉质量都比较平衡的默认值。边界问题有两个来源。第一是图像尺寸不能被blockSize整除直接floor会丢掉最右和最下的像素重建尺寸比原图小。常见做法是镜像填充后再分块重建后裁剪回来。第二是重建后的块边界灰度跳变PCA对每个块独立重建接缝处没有平滑约束k越小越明显。% 镜像填充处理不能被整除的边界 padR blockSize - mod(rows, blockSize); padC blockSize - mod(cols, blockSize); if padR blockSize, padR 0; end if padC blockSize, padC 0; end imgPad padarray(img_gray, [padR padC], symmetric, post); % 对imgPad执行分块PCA, 得到reconPad后裁剪回原尺寸 recon reconPad(1:rows, 1:cols);接缝跳变没有零代价解法加大k能减轻但不能消除因为每个块的均值是逐块独立估计的属于DC分量层面的不连续。彻底消除要改用重叠分块后加权平均但存储量会乘上重叠倍率压缩比下降。对课程设计和大多数比对实验接受轻微接缝、把k调到PSNR超过30dB是性价比最高的选择。4. 彩色图像压缩与重建误差量化4.1 三通道分别PCA与YCbCr处理的差别灰度版本直接扩展到彩色最朴素的做法是对R、G、B三个通道分别调用pca_block_compress再cat回来。问题有两个RGB三通道强相关分开处理等于无视这部分冗余人眼对绿色最敏感、对蓝色最不敏感三通道共用同一个k在感知上不是最优分配。更常见的是先转YCbCr对亮度Y保留较多主成分对Cb、Cr保留较少k。这一步和JPEG的色度下采样是同一个原理人眼对色度高频不敏感。% 彩色图像压缩入口: YCbCr通道独立PCA img imread(lena.png); ycbcr rgb2ycbcr(img); kY 24; kC 8; % 亮度多留, 色度少留 Yc pca_block_compress(double(ycbcr(:,:,1)), 8, kY); CbRe pca_block_compress(double(ycbcr(:,:,2)), 8, kC); CrRe pca_block_compress(double(ycbcr(:,:,3)), 8, kC); reconYCbCr cat(3, Yc, CbRe, CrRe); reconRGB ycbcr2rgb(uint8(reconYCbCr));kY和kC的比例一般取2:1到3:1。色度通道即使只留4到8个主成分肉眼也很难看出偏色但PSNR会明显低于亮度通道所以评估彩色结果时应分开统计三个通道的PSNR而不是对RGB通道误差取平均。RGB直接三通道处理的好处是代码简单缺点是压缩比低YCbCr路径多一次颜色空间转换适合对压缩比有要求的场景。| 方案 | 通道处理 | 典型k分配 | 压缩比收益 | 视觉表现 | | RGB直接三通道 | 每条通道独立PCA | 三通道同一k | 基准 | 色度细节浪费比特 | | YCbCr独立PCA | 亮度多留、色度少留 | Y取2~3倍C | 提升10~20% | 感知质量更好 | | YCbCr加色度降采样 | 色度先降采样再PCA | 在2基础上再减 | 提升30%以上 | 彩色边缘轻微软化 |4.2 PSNR与SSIM作为重建质量的量化指标重建质量的量化不能只看压缩比。PSNR衡量逐像素误差SSIM衡量结构相似性两者配合才能说明「压缩后看起来像不像原图」。PSNR高于30dB通常认为视觉可接受高于35dB很难看出差别SSIM接近0.95以上表示结构保持良好。要区分的是这里讨论的是同分辨率的有损重建和超分辨率重建那种先降采样再放大到高分的问题不是一回事评估口径完全不同。% 计算PSNR和SSIM, 需要Image Processing Toolbox mseVal mean((double(img_gray) - recon).^2, all); psnrVal 10 * log10(255^2 / mseVal); ssimVal ssim(uint8(recon), uint8(img_gray)); fprintf(MSE%.3f PSNR%.2fdB SSIM%.4f\n, mseVal, psnrVal, ssimVal);两个细节决定结果是否正确。mseVal必须用all参数做全数组平均R2018b之后mean(mean(·))会警告且语义不干净。ssim函数要求输入为uint8或值域一致的doublerecon若没裁剪到[0,255]就转uint8超出部分静默截断SSIM会莫名偏高。所以在pca_block_compress末尾保留max(0,min(255,·))那一步是必要的不是可选项。提示SSIM对高斯模糊的容忍度比PSNR高。若PSNR下降但SSIM接近说明误差集中在中高频纹理若PSNR很高但SSIM偏低多半存在结构性块状伪影优先查分块接缝而不是主成分截断。4.3 源码包的目录组织与复用方式这类工程通常会分成四个部分入口脚本、核心函数、量化评估脚本和说明文档。后续换图片时只需要在入口脚本里改文件路径和k值核心函数不必动。pca_image_compress/ ├── main_compress.m # 入口: 读图、调参数、调核心函数 ├── pca_block_compress.m # 灰度分块PCA压缩与重建 ├── pca_color_compress.m # 彩色YCbCr通道封装 ├── compute_metrics.m # PSNR/SSIM/压缩比统计 ├── images/ │ ├── lena.png │ └── test_building.png └── 说明文档.pdf入口脚本的骨架是imread读图→rgb2gray或rgb2ycbcr→调用pca_block_compress→调用compute_metrics→用subplot把原图、重建图、误差图并排显示。k值用第3章的k_95作为初值再手动往小调直到PSNR刚好掉到30dB附近这个位置通常接近率失真曲线的拐点。源码包附带的图片建议统一存成png或bmp避免jpeg的二次压缩块效应混进PCA本身的误差统计。5. 重建图像的验证与调试技巧5.1 用误差图定位分块边界PSNR数字说不清问题在哪误差图能直接暴露空间分布。把abs(原图-重建图)用imagesc画出来正常结果是边缘和高纹理区域亮、平坦区域暗这是高频信息被截断的表现。但如果亮线整齐出现在每个blockSize的倍数位置问题出在分块接缝而不是主成分选择。errMap abs(double(img_gray) - recon); figure; subplot(1,3,1); imshow(uint8(img_gray)); title(原图); subplot(1,3,2); imshow(uint8(recon)); title(重建图); subplot(1,3,3); imagesc(errMap); axis image; colorbar; title(误差图);接缝型误差按顺序排查先确认padarray用的是post方向填pre会让图像整体偏移再确认重建后裁剪尺寸与被填充前一致最后才考虑重叠分块加权平均。如果误差图呈斜向条纹几乎可以断定是reshape与块写入的行列顺序不一致——block(:)按列展开reshape按列填回展开和写回必须都沿列优先方向混用行列顺序就会出现斜纹。5.2 特征向量符号翻转与无损自检eig和svd返回的特征向量符号是任意的同一份数据每次运行可能差一个负号。这不影响重建score和Vk成对翻转后相乘结果一致。但如果压缩端和解压端分开实现两端各自对特征向量做了不同处理重建就会在部分块出现灰度反转。自查方法是保存Vk时同时存一个参考向量加载后对比符号。% 无损自检: kdim时重建应完全一致 reconTest pca_block_compress(img_gray, 8, 64); isIdentical isequal(uint8(reconTest), uint8(img_gray)); fprintf(无损自检通过: %d\n, isIdentical); % 返回0时, 检查padarray裁剪和uint8截断的位置最后给一个适合源码包二次开发的拆法把pca_block_compress拆成compress和decompress两个函数——compress只输出score、Vk、meanX和尺寸信息decompress只接收这四个输入重建图像。拆分后可以在中间对score做量化模拟真实存储位宽对重建的影响。调试顺序固定为先跑通kdim的无损自检再把k调到k_95看PSNR最后用误差图决定是否需要调整blockSize。按这个顺序走分块PCA的绝大多数问题都能定位到具体函数和具体行。本文还有配套的精品资源点击获取