☰
MATLAB图形模式识别:几何特征提取与圆、正方形、矩形分类实战
2026/10/15 16:52:47 网站建设 项目流程

简介:这份资源聚焦图形模式识别中的基础几何形状判别,面向学习数字图像处理、计算机视觉或模式识别课程的学生与开发者,帮助解决圆形、正方形、矩形三类图形的自动识别与分类问题。压缩包共218个文件,约1.07MB,以111个bmp图像样本、32个h头文件、29个cpp源文件为主,另含ico、cur图标资源及txt、doc说明文档,构成一套可直接编译运行的Matlab与C++混合工程,图像样本覆盖多种尺寸与形态,便于测试算法鲁棒性。目前已有2661人学习下载,说明其在教学与实验场景中具有较高参考价值。读者可获得完整的形状特征提取与分类实现思路,包括边界检测、轮廓分析、几何参数判定等关键环节的代码框架,并借助丰富样本快速验证算法效果,适合作为课程设计、毕业设计或自学练手的实践素材。

1. 从一堆散点里认出圆和方:这套 MATLAB 图形模式识别资源到底能干什么

如果你手头有一批二值化后的散点图、轮廓图或者工业相机拍下来的零件截面图,想快速判断每个连通区域到底是圆、正方形还是矩形,那这套基于 MATLAB 的图形模式识别资源就是冲这个场景来的。它不依赖深度学习工具箱,纯靠几何特征加分类逻辑,把「形状识别」这件事拆成可解释、可调参、可复现的几步。适合谁?做课程设计的学生、需要快速验证形状分类思路的算法工程师、以及不想为了一个简单分类任务去搭神经网络环境的从业者。核心思路一句话:先提取区域的几何特征,再用规则或简单分类器把圆、正方形、矩形分开。听起来简单,但真动手做,坑比想象中多——比如旋转后的正方形算不算正方形、矩形长宽比阈值怎么定、噪声导致的边缘毛刺怎么处理。这套资源的价值就在于把这些边界情况都摆出来了,你拿到手改几个参数就能跑自己的图。

2. 形状特征怎么提:从二值图到可分类的数字

2.1 为什么选几何特征而不是模板匹配

模板匹配对旋转和缩放极其敏感,你拿一个标准圆模板去套,图像稍微转一下或者大小变一点,匹配分数就崩了。几何特征不一样,它描述的是形状本身的属性,跟位置、方向、尺度基本无关。常见做法是提取以下几类特征:面积、周长、圆形度、矩形度、长宽比、欧拉数。这套资源里主要用前五个,欧拉数用来过滤孔洞干扰。

圆形度公式是4*pi*Area/Perimeter^2,理想圆等于1,正方形约0.785,细长矩形更低。矩形度是Area/(BoundingBox_W*BoundingBox_H),理想矩形接近1,圆约0.785。长宽比直接拿外接矩形宽高比。这三个特征组合起来,圆、正方形、矩形的区分度就出来了。

提示:特征计算前一定要做形态学去噪,否则周长会被边缘毛刺撑大,圆形度直接失真。

2.2 完整特征提取代码与参数说明

下面这段代码是资源里的核心特征提取函数,输入是二值图像,输出是每个连通区域的特征向量。

function features = extractShapeFeatures(BW) % 去除小面积噪声,面积阈值根据图像分辨率调整 BW = bwareaopen(BW, 50); % 填充孔洞,避免欧拉数异常 BW = imfill(BW, 'holes'); % 获取连通区域属性 stats = regionprops(BW, 'Area', 'Perimeter', 'BoundingBox', 'EulerNumber'); num = length(stats); features = zeros(num, 5); % [面积, 圆形度, 矩形度, 长宽比, 欧拉数] for i = 1:num area = stats(i).Area; perim = stats(i).Perimeter; bbox = stats(i).BoundingBox; % 圆形度:理想圆为1 circularity = 4 * pi * area / (perim^2); % 矩形度:区域面积与外接矩形面积之比 rectness = area / (bbox(3) * bbox(4)); % 长宽比:取长边比短边,始终>=1 aspect = max(bbox(3), bbox(4)) / min(bbox(3), bbox(4)); features(i, :) = [area, circularity, rectness, aspect, stats(i).EulerNumber]; end end

逻辑说明:bwareaopen的面积阈值50是个经验值,图像分辨率高就调大,比如200。imfill填孔是因为有些图形内部有噪点导致欧拉数变成0,影响后续判断。regionprops的Perimeter默认用8连通计算,会略微偏大,如果对精度要求高可以改用bwperim手动算。圆形度和矩形度都在0到1之间,长宽比从1往上走。欧拉数正常实心图形是1,有孔洞就是0或负数。

参数怎么改:面积阈值看你的目标图形最小像素数,一般取最小图形面积的1/3。圆形度计算里如果周长偏大,可以先把边缘做一次平滑,用imopen加小结构元素。

2.3 特征归一化与可视化检查

提取完特征别急着分类,先画散点图看看分布。用scatter3把圆形度、矩形度、长宽比三个维度画出来,圆、正方形、矩形应该各自聚团。如果混在一起,说明特征区分度不够,得加特征或者换特征。

% 假设 features 是 N x 5 矩阵 figure; scatter3(features(:,2), features(:,3), features(:,4), 30, 'filled'); xlabel('圆形度'); ylabel('矩形度'); zlabel('长宽比'); grid on;

这一步是血泪经验:很多新手直接上分类器,结果准确率上不去,回头一看特征分布根本就是糊的。可视化检查花不了两分钟,能省你半天调参时间。

3. 分类逻辑怎么定:规则阈值还是简单分类器

3.1 规则分类的阈值设定与边界情况

最直接的办法是拍阈值。根据几何定义:圆形的圆形度接近1,矩形度约0.785;正方形的圆形度约0.785,矩形度接近1,长宽比接近1;矩形的矩形度接近1,长宽比明显大于1。所以规则可以这么写:

function label = classifyShape(feat) circularity = feat(2); rectness = feat(3); aspect = feat(4); if circularity > 0.85 && rectness < 0.85 label = 'Circle'; elseif rectness > 0.85 && aspect < 1.2 label = 'Square'; elseif rectness > 0.85 && aspect >= 1.2 label = 'Rectangle'; else label = 'Unknown'; end end

逻辑说明:圆形度大于0.85且矩形度低于0.85判圆,这是因为理想圆矩形度0.785,正方形矩形度1,用0.85做分界刚好把两者分开。正方形要求长宽比小于1.2,这是容忍旋转和轻微形变。矩形要求长宽比大于等于1.2,但实际用的时候1.2到1.5之间可能是接近正方形的矩形,看你的业务怎么定义。

参数怎么改:圆形度阈值0.85可以降到0.8,但会引入更多误判。长宽比阈值1.2是经验值,如果你的矩形都是细长的,可以提到1.5。注意旋转45度的正方形,外接矩形会变大,矩形度会掉到0.5左右,这时候规则分类会把它判成Unknown。解决办法是加一个最小外接矩形特征,用regionprops的MinFeretDiameter和MaxFeretDiameter算方向无关的长宽比。

3.2 用最小外接矩形解决旋转问题

旋转是规则分类最大的坑。一个正方形转45度,外接矩形变成菱形框,矩形度和长宽比全乱。正确做法是用最小外接矩形,MATLAB里没有直接函数,但可以用regionprops的MinFeretDiameter和MaxFeretDiameter近似,或者用凸包加旋转卡壳自己写。

% 用 Feret 直径算方向无关的长宽比 stats = regionprops(BW, 'MinFeretDiameter', 'MaxFeretDiameter'); feretAspect = stats.MaxFeretDiameter / stats.MinFeretDiameter;

这个比值对旋转不敏感,正方形始终接近1,矩形始终大于1。把feretAspect替换掉原来的aspect,旋转问题基本解决。代价是计算量稍大,但对几百个区域来说无所谓。

3.3 简单分类器兜底:kNN 与决策树

如果规则阈值怎么调都达不到要求,就上简单分类器。资源里给了两个选项:kNN和决策树。kNN不用训练,把标注好的特征向量存起来,新样本算距离找最近的k个投票。决策树可解释性好,能直接看到分裂条件。

% 假设 trainFeat 是训练特征,trainLabel 是对应标签 knnModel = fitcknn(trainFeat, trainLabel, 'NumNeighbors', 3); treeModel = fitctree(trainFeat, trainLabel); % 预测 predKNN = predict(knnModel, testFeat); predTree = predict(treeModel, testFeat);

参数说明:kNN的k取3到5,样本少就取3。决策树用默认参数就行,fitctree会自动选分裂特征。注意训练集要覆盖各种旋转角度和尺度,否则分类器学到的边界不完整。常见做法是每个类别至少准备30个样本,旋转角度从0到90度每15度一个,尺度缩放0.8到1.2。

4. 避坑与排查:形状识别里那些翻车现场

4.1 现象:圆形被识别成正方形,圆形度算出来只有0.6

原因:边缘毛刺导致周长偏大。二值化阈值选得不好,边缘呈锯齿状,regionprops算周长时把每个锯齿都算进去,周长可能比真实值大30%以上,圆形度直接掉到0.6。

解决:先做形态学开运算平滑边缘,结构元素用strel('disk', 2)。或者改用bwperim提取单像素边缘后再算周长,能减少锯齿影响。更彻底的办法是用imfilter做高斯平滑再二值化,但会轻微改变形状,看你能不能接受。

4.2 现象:正方形旋转45度后被判成Unknown

原因:外接矩形随旋转变化,矩形度和长宽比都失效。

解决:换用Feret直径算长宽比,或者用最小外接矩形。如果非要用外接矩形,就先把区域旋转回去,用regionprops的Orientation属性得到主轴角度,旋转校正后再算特征。但旋转校正会引入插值误差,不如Feret直接。

4.3 现象:两个图形粘在一起,被当成一个矩形

原因:二值化后连通区域合并,bwareaopen去不掉,因为合并后的面积更大。

解决:用分水岭分割或者距离变换加局部最大值找种子点。MATLAB里用bwdist算距离变换,imregionalmax找种子,再watershed分割。这套资源里没直接给分割代码,但这是形状识别绕不开的前置步骤,建议单独花时间处理。

4.4 现象:矩形长宽比阈值设成1.2,但实际矩形是1.15,被判成正方形

原因:阈值太硬,没有容差带。

解决:在1.1到1.3之间设一个过渡区,落在这个区间的输出「Square or Rectangle」让上层逻辑决定,或者引入第二个特征辅助判断。比如矩形度在过渡区里如果大于0.95,更可能是矩形,因为正方形的矩形度通常略低。

4.5 现象:kNN分类器训练集准确率100%,测试集只有60%

原因:训练样本和测试样本来自同一批图,特征分布太像,过拟合了。

解决:训练集和测试集按图像分,不要按区域随机分。同一张图里的区域特征相关性高,随机分会导致信息泄漏。正确做法是留出整张图做测试,训练集用另外的图。如果图太少,就用交叉验证,crossval函数可以帮你做。

5. 进阶技巧:把识别结果反标回原图并批量验证

5.1 结果可视化与标注

识别完了得能看见效果,不然调参就是盲调。下面这段代码把分类结果画回原图,每个区域标上类别和置信度(规则分类就用特征值当置信度参考)。

function visualizeResult(img, BW, labels, features) figure; imshow(img); hold on; stats = regionprops(BW, 'Centroid'); for i = 1:length(stats) c = stats(i).Centroid; text(c(1), c(2), labels{i}, 'Color', 'r', ... 'FontSize', 12, 'FontWeight', 'bold'); % 在标题里显示特征值,方便排查 fprintf('区域%d: 圆形度=%.2f 矩形度=%.2f 长宽比=%.2f -> %s\n', ... i, features(i,2), features(i,3), features(i,4), labels{i}); end hold off; end

逻辑说明:Centroid拿到区域中心,text把标签写上去。fprintf输出特征值到命令行,调参的时候盯着这些数字看,比看图更直接。如果某个区域标签不对,看它的特征值落在哪个区间,就知道该调哪个阈值。

5.2 批量测试与准确率统计

单张图调好了不算数,得批量跑。准备一个文件夹,里面放若干张测试图,每张图对应一个标注文件(可以用MAT文件存真实标签),然后循环跑。

imageFiles = dir('test_images/*.png'); correct = 0; total = 0; for k = 1:length(imageFiles) img = imread(fullfile('test_images', imageFiles(k).name)); BW = imbinarize(rgb2gray(img)); feats = extractShapeFeatures(BW); labels = cell(size(feats,1),1); for i = 1:size(feats,1) labels{i} = classifyShape(feats(i,:)); end % 加载真实标签并比对 load(fullfile('test_labels', [imageFiles(k).name(1:end-4) '.mat'])); for i = 1:length(labels) total = total + 1; if strcmp(labels{i}, trueLabels{i}) correct = correct + 1; end end end fprintf('总准确率: %.2f%%\n', correct/total*100);

参数说明:trueLabels是提前标好的元胞数组,顺序要和regionprops返回的顺序一致。如果不一致,用区域中心点匹配,别用索引。准确率低于80%就回去看混淆矩阵,MATLAB里用confusionmat函数,能直接告诉你哪类被误判成哪类。

5.3 一个容易被忽略的细节:二值化阈值对特征的影响

同一张图,imbinarize用不同方法(Otsu、自适应、手动阈值)得到的二值图差异很大,特征值跟着变。我一般会固定一种二值化方法,然后在特征提取前加一步imadjust增强对比度。如果图像光照不均,用adapthisteq做自适应直方图均衡,再二值化,边缘会干净很多。这个习惯是从一次翻车经历来的:同一批零件图,上午拍的下午拍的光照不一样,Otsu阈值自动变,圆形度从0.9掉到0.7,分类全乱。从那以后我每次做形状识别都强制走一遍光照归一化,再进特征提取。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询