news 2026/10/15 16:52:34

MATLAB图形模式识别实战:圆、正方形、矩形自动分类

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MATLAB图形模式识别实战:圆、正方形、矩形自动分类

简介:这份资源聚焦图形模式识别中的基础几何形状检测,面向正在学习数字图像处理、计算机视觉或MATLAB编程的本科生、研究生及工程技术人员,帮助解决圆形、正方形与矩形自动识别与分类的算法实现问题。压缩包共218个文件,约1.07MB,以111个bmp样本图像为核心数据,配合29个cpp源文件、32个头文件及工程配置、资源脚本等,构成一套可直接编译运行的完整工程,便于读者对照代码理解形状特征提取与分类流程。资源中已积累2661人学习下载,说明其在同类基础视觉任务中具有较高的参考价值。读者可从中获取形状识别算法的完整实现思路、样本图像组织方式以及工程目录结构,适合作为课程设计、毕业设计或入门视觉项目的参考素材,也可在此基础上替换样本、调整参数以适配自己的识别需求。

1. 图形模式识别入门:用 MATLAB 把圆、正方形、矩形从图像里认出来

工业分拣线上,摄像头拍下一堆零件,圆垫片、方垫片、长条料混在传送带上,人眼分得清,但让程序自动分类,很多人第一反应是上深度学习。其实在形状规整、背景可控的场景里,MATLAB 的图形模式识别用几十行代码就能跑通,圆、正方形、矩形这三类几何形状的区分,靠的是区域几何特征而不是像素级纹理。这套方案适合产线质检、教学演示、零件计数这类需求,输入是一张灰度图或二值图,输出是每个连通区域的形状标签。它不依赖训练数据,不需要 GPU,参数可解释,调起来心里有数。下面把我实际做过的流程拆开讲,从预处理到特征提取再到分类判定,每一步都给出可复现的代码和参数含义。

2. 图像预处理与二值化:把形状从背景里干净地切出来

形状识别翻车,十有八九不是分类器的问题,而是二值化没做好。背景有反光、零件有阴影、边缘有毛刺,这些都会让后续的圆形度、矩形度计算失真。我一般把预处理拆成四步:灰度化、去噪、二值化、形态学清理。每一步都有对应的 MATLAB 函数,参数需要根据实际图像调整,没有万能值。

2.1 灰度化与去噪的选型理由

彩色图像先转灰度,用rgb2gray。如果图像本身就是灰度图,跳过这步。去噪用中值滤波medfilt2,窗口大小一般取 3×3 或 5×5。为什么不用高斯滤波?高斯会把边缘模糊掉,而形状识别恰恰依赖边缘的锐利程度。中值滤波在去除椒盐噪声的同时能保留边缘,这是血泪经验——早期我用高斯,圆形度算出来总是偏低,后来换成中值就稳了。

% 读取图像并转灰度 img = imread('parts.png'); if size(img, 3) == 3 gray = rgb2gray(img); else gray = img; end % 中值滤波去噪,窗口3x3 gray = medfilt2(gray, [3 3]); % 显示预处理结果 figure; imshow(gray); title('去噪后的灰度图');

medfilt2的第二个参数是窗口尺寸,必须是奇数。3×3 适合噪声不严重的图,5×5 适合噪声密集但形状较大的图。窗口越大,计算越慢,边缘保留能力越弱,一般不超过 7×7。

2.2 二值化阈值的选择与自适应方法

二值化是分水岭。全局阈值imbinarize配合graythresh(Otsu 法)在光照均匀时够用,但产线图像往往有明暗渐变,这时候必须用自适应阈值imbinarize(gray, 'adaptive')。自适应阈值有两个关键参数:Sensitivity和NeighborhoodSize。前者控制阈值偏移,默认 0.5,值越大越容易把暗区判为前景;后者是局部窗口大小,一般取图像短边的 1/8 到 1/4。

% 全局阈值(光照均匀时用) level = graythresh(gray); bw_global = imbinarize(gray, level); % 自适应阈值(光照不均时用) bw_adaptive = imbinarize(gray, 'adaptive', ... 'Sensitivity', 0.45, ... 'NeighborhoodSize', 2*floor(size(gray,1)/16)+1); % 对比显示 figure; subplot(1,2,1); imshow(bw_global); title('全局阈值'); subplot(1,2,2); imshow(bw_adaptive); title('自适应阈值');

NeighborhoodSize必须是奇数。如果图像是 640×480,短边 480,除以 16 得 30,取奇数 31。这个窗口太小会把噪声当形状,太大则退化成全局阈值。我一般先用默认值跑一遍,看二值图里形状是否完整、背景是否干净,再微调。

2.3 形态学清理:填洞、去小面积、平滑边缘

二值化后常见三类脏东西:形状内部有空洞、背景有零星噪点、边缘有毛刺。对应三个操作:imfill填洞、bwareaopen去小面积、imopen开运算平滑边缘。顺序很重要,先填洞再去噪点,最后平滑。

% 填洞 bw = imfill(bw_adaptive, 'holes'); % 去除小于500像素的连通区域 bw = bwareaopen(bw, 500); % 开运算平滑边缘,结构元素为半径3的圆盘 se = strel('disk', 3); bw = imopen(bw, se); % 显示清理后的二值图 figure; imshow(bw); title('形态学清理后的二值图');

bwareaopen的面积阈值根据实际零件大小定。如果最小零件在图像里占 800 像素,阈值设 500 能去掉噪点又不会误删。strel('disk', 3)的半径 3 适合边缘毛刺不严重的图,毛刺多可以加到 5,但太大会把尖角磨圆,影响矩形判定。

3. 区域特征提取:圆形度、矩形度、长宽比怎么算

二值图干净之后,用regionprops提取每个连通区域的几何特征。区分圆、正方形、矩形,核心看三个指标:圆形度、矩形度、长宽比。这三个指标组合起来,规则简单但边界清晰。

3.1 regionprops 的常用属性与含义

regionprops返回的结构体里,我常用的属性有:Area(像素面积)、BoundingBox(外接矩形)、Perimeter(周长)、Centroid(质心)、MajorAxisLength(长轴)、MinorAxisLength(短轴)。这些属性直接支撑后续计算。

% 提取连通区域属性 stats = regionprops(bw, 'Area', 'BoundingBox', 'Perimeter', ... 'Centroid', 'MajorAxisLength', 'MinorAxisLength'); % 显示区域数量 fprintf('检测到 %d 个连通区域\n', length(stats));

Perimeter的计算基于边界像素链码,对边缘毛刺敏感。如果二值图边缘不平滑,周长会偏大,导致圆形度偏低。这也是为什么前面要做开运算。

3.2 圆形度计算与圆的判定阈值

圆形度公式:Circularity = 4 * pi * Area / Perimeter^2。理想圆为 1,越接近 1 越像圆。实际图像里,圆形的圆形度一般在 0.85 以上,正方形在 0.75 到 0.85 之间,矩形更低。但这不是绝对的,取决于分辨率和边缘质量。

% 计算每个区域的圆形度 for k = 1:length(stats) area = stats(k).Area; perim = stats(k).Perimeter; circularity = 4 * pi * area / (perim^2); stats(k).Circularity = circularity; end % 打印圆形度 for k = 1:length(stats) fprintf('区域 %d: 圆形度 = %.3f\n', k, stats(k).Circularity); end

阈值怎么定?我一般先跑一批已知形状的图,统计圆形度的分布,取圆和非圆之间的谷值。常见做法是圆形度大于 0.88 判为圆。如果图像分辨率低,圆边缘锯齿严重,阈值可以降到 0.82。

3.3 矩形度与长宽比:区分正方形和矩形的关键

矩形度公式:Rectangularity = Area / (BoundingBox宽 * BoundingBox高)。正方形和矩形的矩形度都接近 1,所以单靠矩形度分不开。这时候看长宽比:AspectRatio = MajorAxisLength / MinorAxisLength。正方形长宽比接近 1,矩形明显大于 1。

% 计算矩形度和长宽比 for k = 1:length(stats) bbox = stats(k).BoundingBox; rectness = stats(k).Area / (bbox(3) * bbox(4)); aspect = stats(k).MajorAxisLength / stats(k).MinorAxisLength; stats(k).Rectangularity = rectness; stats(k).AspectRatio = aspect; end % 打印结果 for k = 1:length(stats) fprintf('区域 %d: 矩形度 = %.3f, 长宽比 = %.3f\n', ... k, stats(k).Rectangularity, stats(k).AspectRatio); end

判定逻辑:圆形度 > 0.88 判为圆;否则看长宽比,小于 1.2 判为正方形,大于 1.2 判为矩形。这个 1.2 是经验值,实际要根据零件规格调整。如果正方形零件有倒角,长宽比可能到 1.15,阈值就得放宽。

4. 分类判定与可视化:把结果标回原图

特征算完,分类就是几个 if-else。但怎么把结果直观地展示出来,方便验证和调试,这一步不能省。我习惯在原图上画边界框和标签,同时输出一个结果表格。

4.1 分类逻辑的代码实现

分类函数独立出来,输入是 stats 结构体,输出是每个区域的标签。这样方便后续换阈值或加新形状。

% 分类判定 labels = cell(length(stats), 1); for k = 1:length(stats) circ = stats(k).Circularity; aspect = stats(k).AspectRatio; if circ > 0.88 labels{k} = '圆形'; elseif aspect < 1.2 labels{k} = '正方形'; else labels{k} = '矩形'; end end % 输出分类结果 for k = 1:length(stats) fprintf('区域 %d: %s\n', k, labels{k}); end

这段代码里,圆形度阈值 0.88 和长宽比阈值 1.2 是两个需要根据实际数据调的参数。建议先用一批标注好的图跑一遍,看混淆矩阵,再决定往哪边偏。

4.2 在原图上叠加边界框与标签

可视化用rectangle画框,text写标签。注意 MATLAB 的坐标系原点在左上角,BoundingBox的格式是[x, y, width, height],直接传给rectangle即可。

% 在原图上叠加结果 figure; imshow(img); hold on; for k = 1:length(stats) bbox = stats(k).BoundingBox; % 画边界框 rectangle('Position', bbox, 'EdgeColor', 'r', 'LineWidth', 2); % 写标签 text(bbox(1), bbox(2)-10, labels{k}, ... 'Color', 'r', 'FontSize', 12, 'FontWeight', 'bold'); end hold off; title('形状识别结果');

如果标签重叠,可以把text的 y 坐标再往上偏移,或者用Centroid定位标签。rectangle的LineWidth设为 2 在多数分辨率下清晰可见。

4.3 批量处理与结果导出

单张图跑通后,批量处理就是套一个循环。结果导出到 CSV 或 Excel,方便后续统计。

% 批量处理文件夹下的所有图片 files = dir('images/*.png'); results = table(); for i = 1:length(files) img = imread(fullfile('images', files(i).name)); % ... 预处理、特征提取、分类 ... % 假设 labels 和 stats 已生成 for k = 1:length(stats) row = table({files(i).name}, k, labels{k}, ... stats(k).Area, stats(k).Circularity, ... stats(k).AspectRatio, ... 'VariableNames', {'文件名','区域编号','形状','面积','圆形度','长宽比'}); results = [results; row]; end end writetable(results, '识别结果.csv');

writetable导出的 CSV 可以直接用 Excel 打开。如果区域数量多,建议加一列Centroid坐标,方便回溯。

5. 避坑与排查:形状识别里最容易翻车的五个地方

这套流程跑通不难,但要在实际项目里稳定运行,下面五个坑我几乎每个都踩过。按「现象 → 原因 → 解决」写出来,遇到问题可以对照排查。

5.1 圆形度总是偏低,圆被判成正方形

现象:明明是个圆,圆形度算出来只有 0.78,被误判为正方形。原因:二值图边缘有毛刺,周长偏大,圆形度公式分母变大。解决:加强开运算,把strel('disk', 3)改成strel('disk', 5);或者用bwperim提取边界后做平滑再算周长。另一个原因是图像分辨率太低,圆边缘锯齿严重,这种情况只能提高相机分辨率或拉近拍摄距离。

5.2 正方形和矩形分不开,长宽比都在 1.1 左右

现象:正方形长宽比 1.1,矩形长宽比 1.15,阈值 1.2 卡在中间,两边都判错。原因:矩形不够长,或者正方形有倒角导致长轴偏大。解决:不要只看长宽比,结合矩形度一起判。正方形矩形度通常大于 0.95,矩形在 0.90 到 0.95 之间。如果还是分不开,说明这两类形状本身差异太小,需要考虑加其他特征,比如角点数量。

5.3 自适应阈值把背景噪声判成前景

现象:二值图里背景出现大片白色区域,连通区域数量暴增。原因:Sensitivity设得太高,或者NeighborhoodSize太小,局部窗口把噪声放大了。解决:把Sensitivity从 0.5 降到 0.4,NeighborhoodSize增大到短边的 1/8。如果背景本身有纹理,先做一次高斯滤波再二值化,但高斯核要小,3×3 即可。

5.4 连通区域粘连,两个零件被当成一个

现象:两个挨着的零件在二值图里连成一片,regionprops只返回一个区域。原因:二值化后零件之间没有间隙,或者形态学操作把间隙填上了。解决:先做开运算断开细连接,如果还不行,用分水岭分割watershed。分水岭需要先算距离变换bwdist,再找局部极小值。这个操作对参数敏感,建议只在粘连严重时用。

5.5 批量处理时某张图报错,整个循环中断

现象:跑批量脚本时,某张图因为格式问题或全黑导致regionprops返回空,后续代码报错。原因:没有做异常处理。解决:在循环里加try-catch,或者先判断isempty(stats)。另外,读图后用isempty(img)检查是否读取成功。批量处理最怕一张图毁掉整个任务,加个错误日志记录哪张图失败,后续单独处理。

% 批量处理加异常保护 for i = 1:length(files) try img = imread(fullfile('images', files(i).name)); if isempty(img) fprintf('警告:%s 读取为空,跳过\n', files(i).name); continue; end % ... 处理流程 ... catch ME fprintf('错误:%s 处理失败,原因:%s\n', files(i).name, ME.message); continue; end end

6. 进阶技巧:用轮廓匹配和傅里叶描述子提升鲁棒性

前面讲的几何特征法在形状规整时够用,但如果零件有旋转、缩放,或者边缘有缺损,圆形度和矩形度会波动。这时候可以上轮廓匹配。MATLAB 的matchShapes函数基于 Hu 矩,对旋转缩放不敏感,适合做形状相似度比对。另一个更稳的方法是傅里叶描述子,把轮廓转换到频域,取低频分量作为特征,高频噪声直接丢掉。

6.1 用 matchShapes 做形状相似度比对

matchShapes需要两个轮廓,返回一个相似度值,越小越相似。做法是先把标准形状(圆、正方形、矩形)的轮廓存下来,然后对每个待测区域提取轮廓,逐一比对,取最小值对应的类别。

% 提取待测区域轮廓 [B, L] = bwboundaries(bw, 'noholes'); % 标准形状轮廓(预先从模板图提取) % std_circle, std_square, std_rect 为预先存好的边界坐标 % 对每个待测轮廓做匹配 for k = 1:length(B) boundary = B{k}; % 计算 Hu 矩相似度 score_circle = matchShapes(boundary, std_circle, 'Hu'); score_square = matchShapes(boundary, std_square, 'Hu'); score_rect = matchShapes(boundary, std_rect, 'Hu'); [~, idx] = min([score_circle, score_square, score_rect]); shape_names = {'圆形', '正方形', '矩形'}; fprintf('轮廓 %d: %s\n', k, shape_names{idx}); end

matchShapes的第三个参数可选'Hu'或'Convexity',前者基于 Hu 矩,后者基于凸性缺陷。Hu 矩对旋转缩放更稳,推荐用'Hu'。标准轮廓的提取要在同样的预处理流程下做,否则尺度不一致会影响匹配。

6.2 傅里叶描述子的提取与降维

傅里叶描述子的思路:把轮廓坐标表示成复数序列z = x + iy,做 FFT,取前 N 个低频系数,归一化后作为特征向量。N 一般取 10 到 20,太少区分度不够,太多引入噪声。

% 提取傅里叶描述子 function fd = fourier_descriptor(boundary, N) % boundary: 边界坐标,N: 保留的低频系数个数 z = boundary(:,2) + 1i * boundary(:,1); % 复数序列 Z = fft(z); Z = fftshift(Z); % 直流分量移到中心 % 取中心附近的 N 个系数 center = floor(length(Z)/2) + 1; fd = Z(center - floor(N/2) : center + floor(N/2)); fd = abs(fd) / abs(fd(1)); % 归一化 end % 对每个轮廓提取描述子 for k = 1:length(B) fd = fourier_descriptor(B{k}, 15); % 后续可以用 fd 做分类,比如算欧氏距离 end

fftshift把直流分量移到中心,取中心附近的系数就是低频分量。归一化用abs(fd(1))做分母,消除尺度影响。分类时算待测描述子和标准描述子的欧氏距离,取最小。

6.3 两种方法的适用边界与选择建议

几何特征法快、可解释、参数少,适合形状规整、背景干净、实时性要求高的场景。轮廓匹配和傅里叶描述子慢一些,但抗旋转、抗缩放、抗轻微形变,适合零件有朝向变化或边缘不完整的场景。我一般先用几何特征法跑,如果误判率超过 5%,再上傅里叶描述子。实际项目里,两者结合也常见:几何特征做粗筛,傅里叶描述子做精判。

最后说个习惯:每次调完参数,把当前参数和对应的准确率记在一个表格里,下次换批次图像时直接查表,不用从头试。这个习惯帮我省了太多重复劳动。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/15 16:52:30

HART温度变送器直连以太网的工业落地实践

1. 这不是普通温度采集——高温管道变送器直连以太网的底层逻辑在某建材集团下属的水泥熟料生产线车间里&#xff0c;我第一次见到那台安装在回转窑尾气管道上的HART温度变送器时&#xff0c;它表面温度计显示外壳已达85℃&#xff0c;而内部测点正实时监控着420℃以上的高温烟…

作者头像 李华
网站建设 2026/10/15 16:52:27

AT24C02读写实战:从I2C时序到蓝桥杯掉电保存全解析

接到一个做蓝桥杯单片机赛项准备的朋友问起AT24C02&#xff0c;他在这一块卡了好几天&#xff0c;读写数据死活不对&#xff0c;有时候读回来全是0xFF&#xff0c;有时候写完掉电再上电又恢复成初始值。这玩意儿确实坑不少&#xff0c;尤其是竞赛里要求在几分钟内调通、在几秒内…

作者头像 李华
网站建设 2026/10/15 16:51:50

RISC-V E300时钟系统详解:PLL配置与低功耗设计

调过的板子里&#xff0c;十个有八个“看起来是软件问题”的怪毛病&#xff0c;最后都能在时钟树上找到根源。UART出来全是乱码&#xff0c;SPI时序对不上&#xff0c;PWM频率差一大截——第一反应怀疑代码&#xff0c;第二反应怀疑信号完整性&#xff0c;折腾一圈回头才发现&a…

作者头像 李华
网站建设 2026/10/15 16:51:47

通过LiveNVR实现摄像头集中服务器上录像存储备份与回放、以及远程回放现场摄像头sd卡录像或录像机现有录像

通过LiveNVR实现摄像头集中服务器上录像存储备份与回放、以及远程回放现场摄像头sd卡录像或录像机现有录像1、视频流媒体2、录像类型3、录像回看4、查看录像4.1、时间轴视图4.2、列表视图5、相关接口5.1、云端录像接口5.2、设备录像接口6、录像计划7、相关问题7.1、录像存储位置…

作者头像 李华
网站建设 2026/10/15 16:51:43

Qt无触摸屏按键交互:从GPIO到QKeyEvent的完整方案

简介&#xff1a;本资源面向嵌入式开发初学者与树莓派爱好者&#xff0c;提供一套基于QT框架的无触摸屏系统按键交互方案&#xff0c;重点解决无触控设备下如何通过物理按键或模拟按键控制硬件的问题。包内共7个文件&#xff0c;包含2个cpp源文件、1个h头文件、1个ui界面文件、…

作者头像 李华