简介:这份资源是面向Matlab图像处理初学者与工程实践者的基础实例文档,围绕图像读取显示、预处理、边缘检测、区域选择与掩膜、滤波变换、FFT与DCT、Radon投影等常见任务,给出可直接运行的代码示例与参数说明,帮助读者快速建立从函数调用到效果验证的完整认知。压缩包内共1个pdf文件,约5.94MB,内容以实例代码、注释与结果展示为主,便于对照练习和查阅。目前已有2004人学习下载,说明其在入门与课堂实验场景中具有一定参考价值。读者可从中获得图像灰度转换、裁剪与子图显示、roipoly与roifilt2区域处理、fspecial滤波、fft2与dct2变换、imfinfo信息读取等具体操作思路,适合作为课程实验、自学练手和项目预研的速查材料。
1. 从一份 PDF 实例集说起:Matlab 图像处理到底能跑出什么
很多人第一次接触 Matlab 图像处理,不是从教材第一章开始,而是从一份别人整理好的实例 PDF 开始。这份《matlab图像处理基础实例.pdf》就是典型:它没有长篇理论推导,而是把边缘检测、区域选择、频域变换、形态学操作、彩色空间转换这些高频操作,用一段段可直接粘贴进命令窗口的代码串起来。你拿到它,最直接的用途不是“学完图像处理”,而是把它当成一个可执行的实验手册——想验证某个算子效果,翻到对应小节,改两个参数就能看到图。适合谁?适合已经装好 Matlab、能看懂矩阵索引、但面对edge、roipoly、watershed这些函数不知道从哪下手的人。它更像一个“图像处理函数速查 + 效果对照”的落地包,而不是从零讲起的课程。
2. 实例集的技术骨架:函数分组与执行链路
2.1 这份实例集的组织方式
从正文内容看,这份 PDF 并不是按“第几章第几节”严格编排的教材,而是按操作主题松散分组:图像读取与显示、预处理与裁剪、区域选择与掩膜、滤波与变换、形态学与分割、彩色空间处理。每个主题下给出一段或多段 Matlab 命令,多数以imread开头、以imshow或subplot收尾。这种组织方式的好处是“即查即用”,坏处是缺少统一的工程封装——所有代码都跑在脚本或命令窗口里,变量名随意,图像路径写死。如果你打算把它变成可复用的项目,第一步不是逐段跑通,而是先理解它背后的函数分组逻辑,再决定哪些片段值得封装成函数。
从技术栈角度看,它完全依赖 Matlab 基础环境加 Image Processing Toolbox。核心函数可以归成几类:读写显示类(imread、imshow、imfinfo、subimage、subplot)、几何与区域类(imcrop、imresize、imrotate、roipoly、roicolor、poly2mask、roifilt2、roifill)、滤波与变换类(fspecial、filter2、wiener2、medfilt2、fft2、fftshift、dct2、idct2、radon)、形态学与分割类(strel、imerode、imdilate、watershed、imregionalmin)、彩色处理类(rgb2gray、cat、mat2gray、HSI 手工转换)。这些函数构成了整份实例集的“词汇表”,后面所有代码都是它们的组合。
2.2 一条典型执行链路:从读图到边缘图
拿正文里最靠前的边缘检测片段看,它其实已经隐含了一条最小执行链路:
f = imread('lbxx.bmp'); % 读入原始图像,可能是彩色或灰度 a = rgb2gray(f); % 统一转灰度,后续算子大多按单通道处理 [g, t] = edge(a, 'canny'); % Canny 边缘检测,g 是二值边缘图,t 是阈值 imshow(g); % 显示结果这段代码只有四行,但把图像处理实例的通用节奏说清楚了:读入 → 格式归一 → 算子处理 → 显示验证。rgb2gray这一步不是可有可无的,因为edge对彩色图像的处理方式在不同版本里行为不完全一致,先转灰度能避免很多“为什么我的结果和 PDF 不一样”的翻车。edge返回两个值,第二个t是实际使用的阈值,调试时把它打印出来,比盲目调参数有用得多。
同样的链路在区域选择里变成“读入 → 转灰度 → 构造掩膜 → 应用掩膜 → 分屏显示”。比如roipoly那段:
a = imread('onion.png'); c = [200 250 278 248 199 172]; % 多边形顶点的列坐标(x) r = [21 21 75 121 121 75]; % 多边形顶点的行坐标(y) b = roipoly(a, c, r); % 生成二值掩膜,区域内为 1 subplot(121); imshow(a); subplot(122); imshow(b);这里最容易踩的坑是c和r的顺序。roipoly的签名是roipoly(I, c, r),c是列坐标、r是行坐标,和日常说的“x, y”对应,但和矩阵索引“先行后列”相反。写反了不会报错,只会得到一个位置偏移的掩膜,肉眼还不一定立刻发现。我的习惯是先在图上用impixelinfo确认几个点的行列值,再填进c、r。
2.3 频域与形态学片段的依赖关系
频域部分(fft2、fftshift、dct2)和形态学部分(strel、imerode、imdilate、watershed)对前置步骤的依赖更强。FFT 片段里先构造一个 100×100 的零矩阵、在中间挖一个矩形区域置 1,再变换,这是为了得到一个频谱特征清晰的合成图像,方便观察补零和象限搬移的效果。如果你直接拿真实照片跑,频谱会复杂到看不出规律。形态学片段则依赖二值化:graythresh自动求阈值、im2bw转二值、再用不同尺寸的strel('square', n)做腐蚀膨胀。这里的顺序不能乱,先二值再形态学,否则imerode对灰度图的行为是另一套逻辑。
提示:这份实例集里大量使用
figure、subplot做结果对照,跑的时候建议一个主题一个脚本文件,不要全部堆在命令窗口历史里,否则变量互相覆盖,排查起来很痛苦。
3. 把实例跑起来:环境准备与逐段复现
3.1 环境与图像素材的准备
运行这份实例集不需要额外安装包,但需要确认两件事:Matlab 版本带有 Image Processing Toolbox,以及示例图像文件存在。正文里出现的lbxx.bmp、onion.png、football.jpg、kids.tif、trees.tif并不都是 Matlab 自带样例,onion.png和football.jpg在部分版本的安装目录里能找到,lbxx.bmp则明显是作者自己的文件。最稳妥的做法是建一个工作目录,把 PDF 里提到的图像统一放进去,没有的用同类图像替代,但要注意替代图的尺寸会影响imcrop、roipoly里写死的坐标。
% 建议在每个脚本开头统一设置工作路径和清理环境 clear; close all; clc; imgDir = fullfile(pwd, 'images'); % 图像统一放在 images 子目录 addpath(imgDir);clear; close all; clc这三件套看着老套,但在逐段复现实例时非常必要:上一段的figure不关,下一段的subplot编号会接着排,显示结果和 PDF 对不上。addpath把图像目录加进搜索路径,后面imread('onion.png')就不用写全路径。
3.2 区域选择与掩膜操作的复现顺序
区域选择这一组函数建议按roipoly → poly2mask → roifilt2 → roifill的顺序跑,因为它们共享同一套“多边形顶点 + 掩膜”的概念,连着跑能看清每个函数在掩膜基础上多做了什么。先跑roipoly得到交互式或坐标式掩膜,再用poly2mask理解“顶点坐标如何变成二值矩阵”,然后roifilt2把滤波器只作用在掩膜内,最后roifill做区域填充。
a = imread('onion.png'); i = rgb2gray(a); c = [200 250 278 248 199 172]; r = [21 21 75 121 121 75]; b = roipoly(i, c, r); % 掩膜 h = fspecial('unsharp'); % 锐化滤波器 j = roifilt2(h, i, b); % 只在掩膜区域滤波 subplot(121), imshow(i); subplot(122), imshow(j);roifilt2的关键参数是第二个:滤波器核。fspecial('unsharp')生成一个 3×3 的锐化核,你也可以换成fspecial('average', 5)做区域均值模糊,对比效果更明显。注意roifilt2要求输入图像和掩膜尺寸一致,roipoly生成的b默认和i同尺寸,但如果中途对图像做了imresize,掩膜必须同步缩放,否则直接报维度错误。
poly2mask那段是纯坐标到掩膜的转换,不依赖原图:
x = [63 186 54 190 63]; y = [60 60 209 204 60]; bw = poly2mask(x, y, 256, 256); % 在 256×256 画布上生成多边形掩膜 imshow(bw); hold on; plot(x, y, 'b', 'LineWidth', 2); % 把顶点画上去,核对位置poly2mask的后两个参数是输出掩膜的行数和列数,写死 256×256 是为了和示例画布匹配。实际项目里应该用size(I,1)和size(I,2)动态取,避免换图就错位。
3.3 频域变换与滤波的对照实验
FFT 和 DCT 这两组适合放在一起跑,因为它们都在回答“变换后能量怎么分布”。FFT 片段先看补零前后的频谱差异:
f = zeros(100, 100); f(20:70, 40:60) = 1; % 合成一个矩形亮块 subplot(121); imshow(f); F = fft2(f, 256, 256); % 补零到 256×256 再做变换 F2 = fftshift(F); % 把零频搬到中心 subplot(122); imshow(log(abs(F2)), []);fft2(f, 256, 256)的补零不是为了“提高分辨率”,而是为了在频域获得更密的采样点,让频谱图看起来更平滑。fftshift只是显示上的搬移,不改变频谱内容。log(abs(F2))取对数是因为频谱动态范围太大,直接imshow基本一片黑。DCT 片段则演示了“丢弃小系数再重建”:
a = imread('onion.png'); i = rgb2gray(a); j = dct2(i); j(abs(j) < 10) = 0; % 把小系数置零,模拟压缩 k = idct2(j); subplot(131); imshow(log(abs(j)), []), colorbar; subplot(132); imshow(i); subplot(133); imshow(k, [0, 255]);阈值 10 是示例值,改大图像更糊、改小更接近原图,这个参数就是理解 DCT 压缩最直观的旋钮。imshow(k, [0,255])里的显示范围必须给,因为idct2输出是 double,不给范围会按默认映射显示,看起来发灰。
滤波部分filter2的均值滤波要注意除以 255:
i = rgb2gray(imread('onion.png')); k1 = filter2(fspecial('average', 3), i) / 255; k2 = filter2(fspecial('average', 5), i) / 255; k3 = filter2(fspecial('average', 7), i) / 255;fspecial('average', n)生成的核元素和是 1,但filter2对 uint8 输入做相关运算后数值范围会变,除以 255 是为了把结果拉回可显示区间。更规范的做法是先把图像转double再滤波,避免整数溢出。
4. 核心片段拆解:从边缘检测到彩色空间转换
4.1 边缘检测与 Radon 变换的组合
边缘检测本身不复杂,但正文把它和radon放在一起,意图是展示“边缘图还能继续做方向投影”。先看边缘:
a = imread('onion.png'); i = rgb2gray(a); b = edge(i); % 默认 Sobel theta = 0:179; [r, xp] = radon(b, theta); % 沿 180 个角度做投影 figure, imagesc(theta, xp, r); colormap(hot); colorbar; xlabel('\theta(degrees)'); ylabel('x\prime');edge(i)不指定方法时默认用 Sobel,返回二值图。radon对二值边缘图做投影,得到的是“每个角度下边缘沿某方向的累积强度”,imagesc画出来就是一条条亮带。这里theta取0:179是radon的常用角度范围,xp是投影轴坐标。这个组合在检测直线方向时有用,但要注意radon对输入尺寸敏感,换图后xp的范围会变,imagesc的坐标轴要跟着调。
4.2 形态学腐蚀膨胀与分水岭分割
形态学片段的核心是“同一张二值图,换不同尺寸的结构元素,看腐蚀膨胀的力度”:
a = imread('football.jpg'); t = graythresh(a); % Otsu 自动阈值 bw1 = im2bw(a, t); se1 = strel('square', 3); se2 = strel('square', 5); bw2 = imerode(bw1, se1); % 3×3 腐蚀 bw3 = imdilate(bw1, se1); % 3×3 膨胀 bw4 = imerode(bw1, se2); % 5×5 腐蚀 bw5 = imdilate(bw1, se2); % 5×5 膨胀graythresh返回 0 到 1 之间的归一化阈值,im2bw直接接受这个值。strel('square', n)的n必须是奇数,写偶数会报错。腐蚀让亮区收缩、膨胀让亮区扩张,尺寸越大效果越猛。分水岭那段则是在梯度图上找“汇水盆地”:
f = double(rgb2gray(imread('lbxx.bmp'))); hv = fspecial('prewitt'); gv = abs(imfilter(f, hv, 'replicate')); gh = abs(imfilter(f, hv', 'replicate')); g = sqrt(gv.^2 + gh.^2); % 梯度幅值 l = watershed(g); wr = (l == 0); % 分水岭脊线 f(wr) = 255; imshow(uint8(f));watershed输入是梯度图,输出是标签矩阵,标签为 0 的位置就是分割线。imfilter的'replicate'边界选项能减少边缘伪影。这段代码里hv'是转置,得到水平方向的 Prewitt 核,两个方向梯度平方和开根号就是梯度幅值。分水岭容易过分割,正文后面用imregionalmin看局部极小值,就是在解释过分割的来源。
4.3 HSI 彩色空间的手工转换与分量滤波
彩色部分最值得拆的是 HSI 手工转换,因为它没有直接调用rgb2hsi,而是用公式一步步算:
rgb1 = im2double(imread('football.jpg')); r = rgb1(:,:,1); g = rgb1(:,:,2); b = rgb1(:,:,3); i = (r + g + b) / 3; tmp1 = min(min(r, g), b); tmp2 = r + g + b; tmp2(tmp2 == 0) = eps; % 防止除零 s = 1 - 3 .* tmp1 ./ tmp2; tmp1 = 0.5 * ((r - g) + (r - b)); tmp2 = sqrt((r - g).^2 + (r - b).*(g - b)); theta = acos(tmp1 ./ (tmp2 + eps)); h = theta; h(b > g) = 2*pi - h(b > g); % 根据 B 与 G 关系修正色相 h = h / (2*pi); h(s == 0) = 0;这段代码的每个中间变量都对应 HSI 公式的一步:i是亮度,s是饱和度,h是色相。eps出现在两个地方,都是防除零,这是手工实现公式时最容易漏的。得到 H、S、I 后,正文分别对 H 和 S 做均值滤波再转回 RGB,观察“变柔和”和“饱和度降低”两种效果。转回 RGB 的分段公式比较长,核心是按色相区间分三段计算,每段用余弦公式求主分量,另外两个分量由1 - 和得到。这段代码不适合死记,但值得跑一遍,因为它是理解“为什么在 HSI 空间滤波比在 RGB 空间滤波更符合视觉直觉”的最好例子。
注意:手工 HSI 转换里
h(s == 0) = 0这一行不能省,否则灰度区域(饱和度为 0)的色相会是acos算出的无意义值,转回 RGB 时出现彩色噪点。
5. 避坑与排查:跑这份实例集最容易翻车的几处
5.1 图像路径与文件名大小写
现象:imread('onion.png')报“文件不存在”,但明明把图片放在当前目录。原因:Matlab 当前工作目录不是脚本所在目录,或者文件名大小写不一致(Onion.png与onion.png)。解决:脚本开头用cd(fileparts(mfilename('fullpath')))切到脚本目录,或者统一用fullfile拼绝对路径。文件名建议全部小写,避免跨平台问题。
5.2 坐标顺序写反导致掩膜偏移
现象:roipoly或poly2mask生成的掩膜位置和预期差一截,但不报错。原因:把行坐标和列坐标传反了,c应该是列(x),r应该是行(y)。解决:先用impixelinfo在图上确认几个关键点的行列值,再填数组;或者用ginput交互取点,避免手写坐标。
5.3 数据类型与显示范围不匹配
现象:滤波或变换后的图像imshow出来一片白或一片黑。原因:filter2、dct2、idct2输出是 double,数值范围可能超出[0,1]或[0,255],imshow默认按 double 的[0,1]映射。解决:显示时显式给范围,如imshow(k, [0,255]);或者在滤波前im2double,滤波后mat2gray归一化。
5.4 结构元素尺寸与二值化阈值不匹配
现象:imerode后目标几乎消失,或者imdilate后整图连成一片。原因:strel('square', n)的n太大,或者graythresh得到的阈值不适合当前图像。解决:先用imhist看灰度分布,确认阈值合理;结构元素从 3×3 起步,逐步加大,每次subplot对照。
5.5 分水岭过分割
现象:watershed结果分割线密密麻麻,目标被切碎。原因:梯度图噪声多,局部极小值过多。解决:分割前先平滑梯度图(如imfilter均值滤波或medfilt2),或者用imhmin抑制浅极小值,再跑watershed。正文里用imregionalmin显示局部极小值,就是排查过分割的入口。
6. 二次开发:把散落命令整理成可复用函数
这份实例集最大的价值不是让你照着敲一遍,而是给你一批“验证过的参数组合”。真正上手改的时候,我建议做三件事。第一,把重复出现的“读入 → 转灰度 → 处理 → 分屏显示”抽成一个辅助函数,比如showPair(original, processed, titleStr),内部统一subplot(121)和subplot(122),省掉每段都写显示代码。第二,把区域选择那组函数封装成maskDemo(imgPath, c, r),坐标作为参数传入,换图时只改调用处。第三,给滤波和形态学片段加一个参数表,把fspecial的核类型、strel的尺寸、graythresh的阈值都提成变量,方便做对比实验。
function showPair(a, b, nameA, nameB) % 统一的双图对照显示,避免每段重复写 subplot figure; subplot(1,2,1); imshow(a); title(nameA); subplot(1,2,2); imshow(b); title(nameB); end这个函数看着简单,但能省掉大量重复代码,而且保证所有对照图的布局一致,截图写报告时很省事。验证改造是否成功的方法也简单:拿 PDF 里任意三段代码,用封装后的函数重写,输出结果和原代码逐像素对比(isequal或max(abs(diff(:)))),一致就说明封装没改变行为。
还有一个具体技巧:彩色空间那部分的手工 HSI 转换,建议单独存成rgb2hsi_local.m和hsi2rgb_local.m两个函数文件,输入输出都用double类型,内部保留eps防除零。这样后面做“只对 H 分量滤波”的实验时,直接hsi = rgb2hsi_local(img); hsi(:,:,1) = filter2(...); out = hsi2rgb_local(hsi);三行搞定,不用每次复制那一大段公式。转回 RGB 的分段逻辑里,三个区间的边界条件(>=和<)要特别小心,我一般会在函数末尾加一个assert检查输出范围在[0,1],超出就说明分段写漏了。
从那以后我每次拿到这种“实例 PDF”类的资料,都强制自己先抽一个最小可运行脚本、再抽一个对照显示函数,最后才逐段复现——不然命令窗口历史越堆越乱,回头找某段参数只能靠翻 PDF。希望这份拆解能帮你少走几段弯路,把这份实例集真正跑成自己手里的实验底稿。
本文还有配套的精品资源,点击获取