简介:面向C#开发者的OpenCV矩形识别示例工程,内含从图像读取到矩形框绘制的完整实现,适合正在学习计算机视觉、需要快速落地几何检测功能的初学者和中级开发者。项目基于OpenCV的.NET封装(Emgu CV或SharpCV)编写,源码依次演示了灰度转换、高斯滤波去噪、直方图均衡化、Canny边缘检测、Hough变换直线拟合以及findContours轮廓筛选等关键环节,并配有boundingRect坐标提取、面积周长过滤和可视化标记逻辑,支持逐步输出中间结果,方便观察不同预处理操作对识别精度的影响,从而在实际项目中灵活调参。压缩包共261个文件,以53个dll运行库、43个xml配置/注释说明、26个txt文档和10个cs工程代码为主,另有测试图片、NuGet依赖包及工程配置等,整体约76MB,目录结构清晰,适合边看代码边对照流程调试。已有1066人学习下载。参照该工程,开发者能快速掌握OpenCV在C#环境下的调用与封装技巧,并可将这套流程直接扩展为文档扫描、多边形定位、工业检测等场景的图像预处理模块,是一份值得收藏的入门实战素材。
1. 从一张带边框的照片开始:C#和OpenCV做矩形识别到底在解决什么问题
你用手机随手拍一张合同,纸的边缘发虚、透视变形,想自动把合同区域抠出来摆正,再做OCR——这个动作就是矩形识别。工业上位机里更常见:传送带上的工件停在一个固定视野内,视觉系统要找到工件外接矩形,计算中心坐标偏移量。标题里的“C# OpenCV图像矩形识别”,本质就是用C#调OpenCV的轮廓分析能力,从图像中找出候选矩形区域,进一步用透视矫正把它拉成正视图。它解决的痛点是纯规则写死的定位方式扛不住光照、角度变化,而深度学习的标注成本又太高。这篇文章面向C#上位机开发者和OpenCV初学者,从环境搭建到矫正输出给你一条能直接复现的路径,中间会重点讲筛轮廓的顺序、角点排序的坑,以及几个调参数的实用技巧。
2. 先用最小工程跑通检测:OpenCVSharp选型与Canny边缘
在接触轮廓识别之前,先把地基打好。C#调用OpenCV不是简单安装一个库就完事,封装选型、运行时、图像对象生命周期这三件事,任何一个没处理好,都会让后面的矩形识别代码跑不起来或者在内存上翻车。
2.1 为什么C#环境下大多数人都选OpenCVSharp
C#里能调OpenCV的库有好几个,工业现场最常碰到的就是OpenCVSharp和Emgu CV。Emgu CV封装得很“温柔”,很多类型写成struct、接口像C#原生库,但对C++程序员来说,它那套泛型和初始化反而别扭。OpenCVSharp的做法是尽量还原OpenCV的C++ API,用Mat表示图像,用InputArray做输入重载,在调用Cv2.FindContours时参数顺序和原生代码几乎一一对应。
| 对比项 | OpenCVSharp | Emgu CV |
|---|---|---|
| API风格 | 接近OpenCV C++ | 更像纯C#封装 |
| NuGet安装 | 安装后即可用 | 需要额外配置 |
| Mat/Bitmap转换 | 提供转换类 | 自带BitmatToMat |
| 文档与示例 | 可直接参考C++代码 | 看C#示例 |
| 适合场景 | 上位机、工业视觉、算法移植 | 偏教学、复杂类建模 |
在“C# OpenCV图像矩形识别”这个题目下,OpenCVSharp的优势很明显:网上能找到的OpenCV矩形检测示例大多是C++或Python,用OpenCVSharp可以把它们的代码按同样的函数名翻译过来,降低踩坑成本。另外,OpenCVSharp在每个版本发布时都会把原生dll和依赖一起打包到NuGet包里,部署到工控机时不需要像调用C++ DLL那样手动放置一堆配置。
顺带提一个常见纠结点:halcon和opencv的区别。如果你在工厂里做视觉项目,halcon是商业软件,算子丰富但对License管得严;OpenCV免费开源,在C#上位机这种需要在客户端批量部署的场景里,OpenCVSharp几乎成了默认选择。矩形识别这类基础功能,OpenCVSharp足够,不用引入halcon。
2.2 NuGet安装与第一个读图命令
新建一个控制台工程(.NET Framework 4.7.2以上或.NET 6/8都没问题),在包管理器控制台里执行:
Install-Package OpenCvSharp4 Install-Package OpenCvSharp4.runtime.win如果打算在WinForms或WPF里做实时预览,再装一个:
Install-Package OpenCvSharp4.Extensions这个包提供Mat和Bitmap互转的方法。注意OpenCvSharp4本身是平台无关的程序集,实际干活的是runtime.win里的原生dll。如果安装报错,先检查NuGet包源是不是官方源,再确认目标平台不是.NET Framework 3.5这类老框架。另外,装完包以后项目里出现“找不到DllOpenCvSharp”之类的异常,通常是忘了装runtime.win包。
装好之后,用下面这段代码验证环境,顺便把灰度图生成出来:
using OpenCvSharp; Mat src = new Mat("test.jpg", ImreadModes.Color); Console.WriteLine($"图像尺寸: {src.Width}x{src.Height}"); Mat gray = new Mat(); Cv2.CvtColor(src, gray, ColorConversionCodes.BGR2GRAY); Cv2.ImWrite("gray.jpg", gray); src.Dispose(); gray.Dispose();这里有几个新手常忽略的点。new Mat("test.jpg", ImreadModes.Color)是OpenCVSharp重载,文件不存在时会抛出OpenCVException,不要等空引用。灰度图在某些颜色区分度很低的场景里可能把矩形和背景混在一起,遇到这种图可以考虑用HSV的V通道或者Canny时再调参数。ImWrite保存jpg时质量默认是95,如果后面要保存结果图给OCR用,建议用Cv2.ImWrite("result.png", result),PNG无损,避免二次压缩影响文本清晰度。
C#程序员刚接触OpenCVSharp最不习惯的一点是:Mat是非托管对象,一定要自己释放。上面这短短几行如果不调用Dispose,在一轮循环里加载100张图,内存在几分钟内就会占满。后文所有代码我都会尽量用using或手动Dispose,这是矩形识别项目能做到长时间运行的前提。
2.3 Canny边缘检测:找矩形前先让边缘“连成线”
矩形识别的基础是找到图像中灰度值剧烈变化的位置,Canny就是最常用的边缘提取算子。相比Sobel只算梯度方向,Canny做了非极大值抑制和双阈值连接,把边缘细化为单像素宽的白线,噪声区域则被抑制成小黑点。先做平滑再Canny是标准操作:
Mat edges = new Mat(); Cv2.GaussianBlur(gray, gray, new Size(5, 5), 0); Cv2.Canny(gray, edges, 50, 150);高斯核Size(5,5)是经验值。核越小保留细节越多,边缘越碎;核越大矩形长边可能被平滑出“波浪”。Canny的两个阈值,一个决定边缘延续性,一个决定强边缘的筛选,我把它们整理成下面这张表,调参时对着来:
| 参数 | 作用 | 调高后的影响 | 调低后的影响 |
|---|---|---|---|
| 低阈值 | 控制弱边缘连接 | 断边增多,矩形闭合度下降 | 弱边缘全部检出,轮廓杂乱 |
| 高阈值 | 排除纹理与噪点 | 保留最强边缘,可能丢失浅色矩形边 | 大量非目标轮廓进入候选 |
| 高斯核 | 抑制噪声 | 边缘平滑但位置偏差变大 | 边缘精细但断点多 |
这三个参数的组合确实有点玄学,我自己的经验是:背景单一的场景直接50/150;室外照片或亮度不均的车间场景,先把低阈值降到20-30,高阈值放到180-220;如果图像分辨率很高(超过2000万像素),Canny前最好先缩小到原图的一半,边缘提取效果反而更稳定。Canny输出的edges是单通道黑白图,下一步的FindContours就靠它找连通域。
补充一个容易翻车的点:Canny只能找“边缘点”,不能保证这些点构成闭合曲线。矩形的一条边被阴影切断时,Canny结果会出现缺口,后边的轮廓提取就会失败。碰到这种情况别急着调阈值,先在原图上看看是不是光照问题,有条件的加个均值亮度归一化,或者用直方图均衡化Cv2.EqualizeHist(gray, gray)加强对比度,比盲目调Canny参数更有效。跑通这一步后,你已经有了什么?一张原始图、一张灰度图、一张边缘图。下一章开始在edges上找轮廓。
3. 找矩形的主流程:轮廓筛选与多边形逼近
拿到edges之后,矩形识别的核心环节才开场:找出所有连通区域,过滤掉那些明显不是矩形的轮廓,最后把四边形点集提出来。这一步的代码量不大,但筛选顺序决定了最终识别准不准。
3.1 从边缘到轮廓:FindContours返回了什么
OpenCVSharp的FindContours和OpenCV原生长得一样,典型调用是:
Mat hierarchy = new Mat(); OpenCvSharp.Point[][] contours; Cv2.FindContours(edges, out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);edges是上一章Canny的输出。contours是一个二维数组,每一项代表一条轮廓,每个轮廓是一系列点。RetrievalModes.List表示只提取轮廓、不整理嵌套关系,矩形识别用List就够了;如果遇到矩形内部还有表格线、需要排除内嵌轮廓的情况,才用RetrievalModes.External只取最外层轮廓。ContourApproximationModes.ApproxSimple会压缩水平、垂直和对角方向的冗余点,比如一个矩形原本可能有几百个边缘点,压缩后只剩四个角点,这对后续判断非常方便。
这一行的输出是个黑匣子:你看不到轮廓长什么样,只能通过画出来排查。常用调试方法是新建一张黑色Mat,用DrawContours把所有轮廓画成白色,直接看有哪些候选:
Mat contourDraw = new Mat(edges.Size(), MatType.CV_8UC1, Scalar.Black); Cv2.DrawContours(contourDraw, contours, -1, Scalar.White, 1); Cv2.ImWrite("contours_debug.png", contourDraw);如果这一图画出来全是细碎短线,说明Canny阈值偏低或高斯核太小;如果只有几个大块轮廓,说明边缘质量不错。调试图不要删除,后面调筛选参数还需要它做对照。
3.2 用面积先去一波:哪些轮廓可以直接扔掉
在图片上做矩形识别的坑之一,是画面里往往有大量干扰:文字笔画连成一团、背景纹理形成闭合环、桌子边缘混进来。所以拿到contours后第一件事不是多边形逼近,而是算面积。以下代码做了两步筛选:
List<Point[]> candidates = new List<Point[]>(); foreach (Point[] contour in contours) { double area = Cv2.ContourArea(contour); if (area < 1000) continue; if (area > src.Width * src.Height * 0.6) continue; candidates.Add(contour); }第一处面积下限1000,排除噪声形成的小闭合域;第二处面积上限是原图的60%,排除直接被镜头充满的伪矩形,比如窗框或整个桌面。具体下限要根据图像分辨率缩放,100万像素的图用500,1200万像素的图用2000甚至5000。如果你拍摄的矩形本身就比较小,先用周长算尺寸再设阈值,别让阈值把目标误杀。
第二处上限很重要,因为有些场景里目标矩形就是整个画面的一部分,但背景中比它更大的闭合边缘往往会让ApproxPolyDP逼近成四边形,后患无穷。
3.3 用ApproxPolyDP把轮廓逼近成多边形
筛选后的每条轮廓点数量还是很多,需要做多边形逼近。ApproxPolyDP的输入是一条轮廓和一个精度值epsilon,输出是一个顶点更少的多边形。代码:
List<Point2f[]> rectCandidates = new List<Point2f[]>(); foreach (Point[] contour in candidates) { double perimeter = Cv2.ArcLength(contour, true); double epsilon = 0.02 * perimeter; Point[] approx = Cv2.ApproxPolyDP(contour, epsilon, true); if (approx.Length == 4) { Point2f[] quad = approx.Select(p => new Point2f(p.X, p.Y)).ToArray(); double rectArea = Math.Abs(Cv2.ContourArea(quad)); double origArea = Math.Abs(Cv2.ContourArea(contour)); if (rectArea > origArea * 0.8) { rectCandidates.Add(quad); } } }epsilon的值直接决定逼近结果。默认取轮廓周长的2%。epsilon太小,逼近后仍是十几二十个点,找不到四边形;epsilon太大,本来的矩形会被塌缩成三角形甚至一条线。0.02是标准起点,如果你检测的矩形轮廓本身比较平滑,0.01就能拿到四边形;如果边缘有锯齿,0.03才稳定。我一般会写一个从0.01到0.05的循环,返回四点就停,这样能自动适配不同轮廓的粗糙度。
approx.Length == 4是判定矩形的最直接条件,但注意不是四点任意排序都代表矩形——下一章会处理角点排序。rectArea > origArea * 0.8这个比值条件,目的是排除自交的不规则多边形,也确保多边形没有丢掉矩形的大块区域。如果原轮廓面积10000、逼近后面积只有5000,多半是轮廓缺口太深导致逼近时凹进去一大块,这时说明边缘闭合质量不行,得回头调Canny。
任意四点可能构成一个非常扁的平行四边形,比如一个几乎压扁的长条。在矩形识别场景里,真正的文档/卡片/工件的外接矩形长宽比通常落在0.2到5之间。可以用最小外接矩形来验证:
RotatedRect box = Cv2.MinAreaRect(quad); float ratio = box.Size.Width > 0 ? box.Size.Height / box.Size.Width : 0; if (ratio > 0.2 && ratio < 5) { rectCandidates.Add(quad); }MinAreaRect会算出这个点集的最小外接旋转矩形,如果长宽比极端,说明这个四点形状虽然闭合但绝对不是你要的矩形。这里用的是Point2f,因为下一步透视变换只接收浮点坐标。经过面积、顶点数、面积比、长宽比四道筛子之后,rectCandidates里通常就剩一到两个真正的矩形。如果这里候选还是很多,就再加一个凸性判断:Cv2.IsContourConvex(quad),标准矩形必须满足凸性,凹四边形直接扔。
当画面里有多个候选矩形,比如一起拍到了好几张卡片,我习惯按面积排序,取面积最大的那个作为主目标;如果业务上要识别多个,再按面积阈值或离图像中心的距离继续排序。
4. 角点排序与透视矫正:把斜着的矩形摆正
矩形识别做到这里,已经拿到一组四边形的浮点坐标。但要把对应的图像内容“抠下来”,还不能直接透视变换——FindContours返回的点顺序是从左到右、从上到下扫出来的,与矩形左上/右上/右下/左下角没有对应关系。不排序就做矫正,得到的图像会旋转或镜像,这是一个非常隐蔽的踩坑点。
4.1 四个点为什么顺序会乱:自己实现一个角点排序
FindContours得到的四边形顶点顺序,实际上和轮廓的起始点、扫描方向有关。OpenCV自身没有提供直接按“左上、右上、右下、左下”排序的接口,网上那段流传很广的orderCorners代码就是为这个问题写的。常见排法是:先用四个点算出中心,然后把每个点按与中心的关系归类。
private static Point2f[] OrderCorners(Point2f[] corners) { Point2f center = new Point2f( (corners[0].X + corners[1].X + corners[2].X + corners[3].X) / 4, (corners[0].Y + corners[1].Y + corners[2].Y + corners[3].Y) / 4 ); Point2f tl = corners.OrderBy(p => p.X + p.Y).First(); Point2f br = corners.OrderByDescending(p => p.X + p.Y).First(); Point2f tr = corners.Where(p => Math.Abs((p.X + p.Y) - (tl.X + tl.Y)) > 1) .OrderBy(p => p.X - p.Y).First(); Point2f bl = corners.Where(p => Math.Abs((p.X + p.Y) - (br.X + br.Y)) > 1) .OrderByDescending(p => p.X - p.Y).First(); return new[] { tl, tr, br, bl }; }原理并不玄:左上角在所有角点中x+y最小,右下角x+y最大;右上角x-y最大,左下角x-y最小。先用x+y选出tl和br,剩下两个再按x-y区分。中间几个Where条件是为了防止两个角点的x+y值恰巧相等时出错。这一步在OpenCVSharp和Emgu CV里思路一样,你看到网上说的“orderCorners”就是指这个。
排序完之后,建议把角点顺序打印出来人工确认。如果图里矩形旋转超过90度,这个“左上”可能是视觉上的左下,需要根据后续矫正结果决定是否加一个镜像修正。在实际项目中,最稳的验证方式就是做一次透视矫正,看输出内容头是不是朝上。
这里还有一个常见误区:很多人用Cv2.BoxPoints(rotatedRect)直接拿四个角点。BoxPoints返回的是最小外接矩形的四个顶点,它会把原本轻微梯形的文档边缘强行拉成矩形,丢失透视信息。所以矩形识别里我坚持用approxPolyDP逼近出的原始四边形,而不是最小外接矩形的端点。
4.2 用GetPerspectiveTransform和WarpPerspective做矫正
有了排序后的四个点,下一步指定目标尺寸。比如A4纸比例400x600,也可以用原图上四角距离估算宽高比。先看固定尺寸版本:
Size dstSize = new Size(400, 600); Point2f[] dstPoints = new Point2f[] { new Point2f(0, 0), new Point2f(dstSize.Width - 1, 0), new Point2f(dstSize.Width - 1, dstSize.Height - 1), new Point2f(0, dstSize.Height - 1) }; Mat perspectiveMatrix = Cv2.GetPerspectiveTransform(OrderCorners(corners), dstPoints); Mat warped = new Mat(); Cv2.WarpPerspective(src, warped, perspectiveMatrix, dstSize);GetPerspectiveTransform需要至少四个对应点,正好四边形。这里src用原彩色图,这样矫正结果直接就是彩色正视图,适合后面接OCR。WarpPerspective的插值方式默认是线性插值,对文本类图像够用,如果贴图纹理要求更光滑,可以用InterpolationFlags.Cubic。
注意目标尺寸的长宽比要和实际矩形接近,否则输出会被拉伸。可以在原图上用四个角的距离估算宽高比:
double width = Cv2.Norm(corners[0] - corners[1]); double height = Cv2.Norm(corners[1] - corners[2]); Size dstSize = new Size((int)width, (int)height);然后用这个比例做目标尺寸,能保证矫正后物体不变形。如果是要固定输出给OCR引擎,比如身份证识别,建议固定目标尺寸,比例允许轻微变形,OCR对轻微拉伸的适应性通常比大比例变化更好。
怎么验证排序对了?看矫正后的图里文字方向。如果左上和右上调换,矫正图会上下颠倒;如果左上和左下替换,矫正图会水平镜像。用一行文字就能判断,比反复调点方便得多。
4.3 保存与显示:把矫正图像写回文件
最后一步很直接:
Cv2.ImWrite("warped.png", warped); warped.Dispose(); perspectiveMatrix.Dispose(); src?.Dispose();在WinForms里要把Mat转成Bitmap显示,用OpenCvSharp.Extensions里的方法:
using OpenCvSharp.Extensions; Bitmap bmp = BitmapConverter.ToBitmap(warped); pictureBox1.Image = bmp;这里有几个常见问题:BitmapConverter.ToBitmap默认会申请新的托管内存,拷给pictureBox后如果warped被Dispose,Bitmap还能正常显示;如果是从共享内存模式转换,释放Mat后Bitmap会黑屏,所以建议用ToBitmap的拷贝行为,这样更安全。另外,保存路径尽量不要用中文或空格,有些工控机上的dll解析对中文路径支持不好,这是玄学但实际碰到过。
到此,矩形识别以一张矫正好的正视图收尾,误差基本可控。
5. 矩形识别避坑:我踩过的五个常见问题
矩形识别从demo到能交付,中间隔着不少坑。下面这五条是我自己项目中真正遇到过的,按“现象→原因→解决”的格式写出来,希望对你有用。
5.1 现象:Canny边缘断裂,矩形轮廓闭合不了
现象:检测到的矩形总是缺一条边,FindContours后最外层的轮廓是“U”形,approxPolyDP出来是3个点。 原因:光照在矩形边缘产生渐变,或背景与矩形颜色极其接近,导致梯度响应低于低阈值。 解决:先用GaussianBlur加大核到(7,7)试试;把低阈值降到20,高阈值设到180;仍不行就在原图上做直方图均衡化Cv2.EqualizeHist(gray, gray)增强对比度。如果阴影边断裂,可以把Canny的低阈值调到10,让弱边缘连上,代价是多一些噪声,但后续面积筛选能把噪声过滤掉。
5.2 现象:检测出几十个小矩形,目标矩形反而找不到
现象:画面里有表格线、文字边缘、传感器噪点,FindContours后大量小区域也满足四点拟合,导致目标矩形被淹没。 原因:只做了多边形逼近,没做面积和长宽比筛选。 解决:先按面积排序取Top 10,再依次做四边形检测。把面积下限调高到图像总像素的0.02倍。如果画面里有多个类似大小的真实矩形(比如一排工件),就加一个“离图像中心最近”的选择逻辑。
5.3 现象:ApproxPolyDP返回的不是4个点,调epsilon好像没用
现象:同一张图,epsilon=0.02时有时返回5个点、6个点,有时又是4个点。 原因:轮廓本身的锯齿和噪声干扰逼近结果;ApproxPolyDP的epsilon是基于周长百分比,而轮廓噪声导致周长浮动。 解决:先对边缘图做形态学闭运算,把断边接上:Cv2.MorphologyEx(edges, edges, MorphTypes.Close, Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3)))。同时把epsilon改成固定值如0.01*perimeter,如果返回点仍多于4,再取面积最大的一段做进一步逼近。建议写一个循环:从0.01到0.05逐步增大epsilon,返回4个点时立即跳出。
5.4 现象:OpenCVSharp里Mat和Bitmap互转报“内存不足”或黑屏
现象:用BitmapConverter.ToBitmap后图片显示黑屏,或者频繁转换后程序内存暴涨。 原因:OpenCVSharp部分重载的Bitmap操作没有复制像素,而是共享内存;Mat被Dispose后Bitmap变成无效引用。内存暴涨是没有及时释放Mat。 解决:一律用BitmapConverter.ToBitmap做拷贝转换,不要用共享内存重载。Mat用完后立即Dispose,如果是在循环里识别,每帧结束后释放Mat。另外,pictureBox.Image赋值前先Dispose旧Image,避免GDI+句柄泄漏。
5.5 现象:同一张图换了一台机器,识别结果完全变了
现象:代码在开发机上跑得好好的,部署到工控机上后,矩形找不到了。 原因:摄像头分辨率、帧率、色彩空间不同;工控机缺少某个视频解码dll;或OpenCvSharp运行时版本不一致。 解决:先读取src.Width/Height打印尺寸,确认图像被正确加载。然后统一图像流程:无论什么相机,先转成BGR,再Cv2.Resize到统一宽度(比如1280),再做Canny。这样能抹平分辨率差异。如果工控机是32位系统,检查NuGet装的OpenCvSharp4.runtime.win是不是x86版本。
6. 进阶:先缩小再映射,让矩形识别在视频流里也能实时跑
很多上位机场景要连续识别,不是处理一张静态图。如果在原图上直接跑完整流程,一张1920x1080的图可能要200ms,视频卡顿明显。这里有一个性价比极高的技巧:先缩小图像再检测,最后把角点坐标映射回原图。把图像缩到宽度640,缩小的检测时间通常只有原来的1/5,而且边缘更连续。
代码思路:
Mat small = new Mat(); Cv2.Resize(src, small, new Size(640, (int)(src.Height * 640.0 / src.Width))); // 在small上执行Canny、FindContours、ApproxPolyDP, // 拿到角点后,用比例映射回原图 float scaleX = (float)src.Width / small.Width; float scaleY = (float)src.Height / small.Height; for (int i = 0; i < corners.Length; i++) { corners[i] = new Point2f(corners[i].X * scaleX, corners[i].Y * scaleY); }映射之后再按原图坐标做透视矫正,输出精度不受影响,因为矫正时用的是原分辨率。这里要注意:在缩小图上做筛选时的面积阈值,也要按比例缩小,否则原来能通过的矩形在缩小图上面积变小被过滤掉。我一般直接用area > (small.Width * small.Height * 0.01)这样的比例式。
验证结果我习惯看三个指标:一是矫正图的长宽比是否接近真实物体,二是四个角点的透视变换后的误差是否在1像素内,三是连续帧之间角点位移是否稳定。一旦连续帧角点跳变,多数是边缘检测在某一帧断裂,此时不要强行用面积最大,而应该用上一帧的角点做跟踪约束。
关于这个方向值不值得做:矩形识别是很多视觉项目的地基,像文档扫描、仪表读数、流水线定位,都能用它搭出第一版。遇到复杂背景,往深度学习目标检测迁移时,它也可以用来生成训练数据的预处理。最终说一句我的教训:最早我图省事直接在原图上跑,结果边缘断裂、轮廓上千条,被一个面积过滤就卡死。后来学会先缩小、先筛面积、再多边形逼近,整个流程从“能跑”变成“好用”。希望这个思路也帮到你。
本文还有配套的精品资源,点击获取