拓冰建站拓冰建站
首页 / 资讯中心 / 正文

OpenCV基础操作全攻略:从环境配置到图像读写实战

从环境配置到图像读写OpenCV基础操作一篇讲透我接触OpenCV的时间不算短了最早是在一个质检项目里需要快速验证一批产品图片的缺陷检测思路。当时面对一堆图片我做的第一件事不是写算法而是把图像读取、显示、保存、裁剪、缩放这一整套基础操作先跑通。后来带新人我也发现一个共性规律很多人卡住的地方往往不在算法本身而在最基础的图像IO和数据类型上——读进来的是什么为什么显示出来颜色不对为什么窗口一开就卡死这些细节不搞清楚后面学再高深的算法都会觉得别扭。这篇的内容定位是OpenCV图片处理系列的入门篇覆盖从安装环境、图像在内存里的真实结构到读写显示、缩放旋转、裁剪拼接这些最高频操作。目标读者是刚接触OpenCV的初学者以及用Python做图像处理但一直没系统梳理过基础细节的人。我会结合自己实际项目中踩过的坑来写尽量把每个操作背后的原理讲清楚而不是只甩几个API出来。1. OpenCV到底能干什么为什么Python版最适合入门1.1 OpenCV在视觉工具链中的定位OpenCV全称Open Source Computer Vision Library是一个开源的计算机视觉库。从1999年Intel发起到现在由OpenCV.org维护它已经积累了超过2500个优化算法覆盖图像处理、特征检测、目标识别、摄像头标定、三维重建、光流跟踪等几乎全部视觉方向。和市面上的商业视觉软件对比会更直观。工业领域常用的HALCON、Vision Master这类工具优势在于图形化界面、拖拽式流程调试起来非常方便适合产线视觉工程师快速搭建检测方案。但这类软件价格不菲而且算法细节是黑盒你想调整底层逻辑、做定制化改进时灵活性不够。OpenCV则恰恰相反它是开源免费的每个算法、每个函数你都能看到源码参数可以随心所欲调配合Python的科学计算生态做算法验证和原型开发效率极高。1.2 Python版和C版怎么选很多初学者会纠结到底学C版还是Python版。我的建议非常简单先学Python版。C版OpenCV性能确实更强在嵌入式设备、产线实时检测这类对延迟敏感的场景中是不可替代的。但C的开发调试链路长写一个窗口显示都要涉及头文件配置、编译环境学习曲线陡峭。Python版调用了同一套底层C/C实现API逻辑完全一致但省去了编译的烦恼你可以把全部精力放在理解图像处理本身。还有一个更现实的原因现在的深度学习推理框架PyTorch、TensorFlow都是Python生态模型的预处理、后处理几乎都用Python写。你在Python里学到的图像处理方法做深度学习项目时随手就能用上。等你把基础概念吃透了再去补C版的工程化能力会发现非常顺畅。1.3 这篇会给你什么作为系列第一篇我聚焦在图像处理的地基环境搭建、图像数据结构、读写显示、基础几何变换。这些内容看似琐碎但每一条都是后面学习颜色识别、人脸检测、轮廓提取时绕不开的前提。比如你后面做颜色识别时发现HSV和RGB傻傻分不清就是本章要讲的BGR问题没吃透做批量处理时图片读不进来就是路径和flag的细节出了问题。2. 让OpenCV跑起来安装和版本差异里藏着的坑2.1 最省心的pip安装方式Python环境下安装OpenCV最简单的方式就是pip安装。pip install opencv-python装完之后import的名字是cv2不是cv。这也是一个常见的疑惑为什么OpenCV的Python包叫cv2历史上OpenCV曾经有cv和cv2两个版本cv2代表着基于C接口的新架构。虽然现在cv已经废弃但import的名字沿用了下来所以看到import cv2别觉得奇怪。如果想要用到更多扩展模块比如SIFT、SURF这些特征算子还需要安装扩展包pip install opencv-contrib-python这里有一个非常重要的坑opencv-python和opencv-contrib-python不要同时装在同一个环境里否则会出现模块冲突、函数覆盖等问题。如果你不确定需要哪些功能最稳妥的选择是直接安装opencv-contrib-python一个包就够了它包含核心模块加扩展模块。2.2 版本不一致导致的numpy报错OpenCV在底层对numpy有依赖图像读进来之后就是以numpy数组形式存在的。所以numpy版本不合适时会出现各种莫名其妙的问题最常见的报错是module numpy has no attribute XXX或者ValueError: ndarray is not C-contiguous。这个坑在使用Anaconda时尤其容易踩。Anaconda自带的numpy版本可能和OpenCV要求的版本不一致尤其是当你用conda的源安装numpy时可能装到比较旧的版本。我的建议是在装了OpenCV的虚拟环境里主动升级numpy到较新版本正常情况下都能兼容。pip install --upgrade numpy2.3 安装验证方法安装完之后用一段极简代码验证环境是否正常import cv2 print(cv2.__version__) # 查看版本号 print(cv2.getBuildInformation()) # 查看编译信息如果正常打印出版本号比如4.8.0说明安装成功。getBuildInformation()会输出很长的编译配置信息包括是否支持CUDA、是否启用了FFMPEG等后面的GPU加速会用到。2.4 要不要自己编译CUDA版OpenCV搜OpenCV教程时你一定会看到Linux安装CUDA版本OpenCV、OpenCV编译CUDA这类帖子。这里我给出一个明确的判断标准如果你只是学习图像处理基础、做算法验证完全不需要自己编译。pip安装的预编译版本在CPU上已经足够快而且省心。但如果你要做深度学习模型的实时推理加速或者在高分辨率视频流上跑检测算法CPU版本可能成为瓶颈这时候才需要考虑编译CUDA版让OpenCV的某些算子直接调用GPU并行计算。自己编译OpenCV确实比较痛苦依赖库多、编译时间长中间任何一个依赖版本不对都会导致失败。我的建议是先跑通CPU版本把手头的项目做完再根据性能测试结果决定要不要花这个精力。2.5 PyCharm和Anaconda环境配置的常见问题很多初学者用PyCharm时遇到ModuleNotFoundError: No module named cv2本质原因就一个PyCharm当前解释器和你安装OpenCV的解释器不是同一个。尤其是电脑上装了多个Python版本或者Anaconda和系统Python并存时PyCharm默认用了其中一个解释器而pip装到了另一个。解决方法在PyCharm的File - Settings - Project - Python Interpreter里把解释器切换到安装过OpenCV的那个环境。如果用的是Anaconda就在Conda环境里找到对应的env路径。这个错误本身没有任何技术难度纯粹是环境路径问题。3. 图像在OpenCV内部就是个数字矩阵Mat与numpy原理3.1 图像的数学本质在我带新人的时候最常被问的问题之一就是图像在OpenCV里到底是什么。答案说穿了很简单图像就是一个由数字组成的矩阵。拿一张灰度图来说它只有两个维度行和列。每个位置上的数值代表了该像素点的亮度0表示纯黑255表示纯白中间的数值就是不同程度的灰。比如一个4x4的灰度图像在内存里就是这样的数组import numpy as np # 模拟一张4x4的灰度图值越低越黑 img_gray np.array([ [0, 64, 128, 255], [32, 96, 160, 224], [64, 128, 192, 255], [128, 160, 192, 224] ], dtypenp.uint8)彩色图像则是在每个像素点上多了通道的概念最常见的是RGB三通道也就是每个像素由红、绿、蓝三个数值共同决定数值范围都是0到255。三通道叠加就能组合出人眼能看到的绝大部分颜色。3.2 BGR和RGBOpenCV的颜色顺序是个历史坑这是初学者碰到的最经典的问题一张图片用OpenCV读进来再显示出来颜色和你用看图软件打开时不一样——红色和蓝色对调了。原因在于OpenCV默认的颜色顺序是BGR而不是我们习惯的RGB。这个坑的根源要追溯到OpenCV早期的设计决策。当时它从Camera采集、图像编码这块继承下来的接口就是BGR顺序后为了保持兼容就一直沿用至今。而Python的matplotlib、PIL这些库用的是RGB顺序所以两边对接时经常出现色相错乱。如果需要从BGR转到RGB用cv2.cvtColor做颜色空间转换import cv2 img cv2.imread(image.jpg) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB)凡是和PIL、matplotlib、深度学习框架交互的场景基本都要先做这一步。知道这个原理颜色不对这个问题就不会再困扰你了。3.3 坐标系和shape属性宽高顺序千万别搞反图像坐标系也是新手容易模糊的点。OpenCV中图像的原点在左上角x轴向右y轴向下。用shape属性查看图像维度时返回的是一个三元组import cv2 img cv2.imread(image.jpg) h, w, c img.shape print(f图像高度: {h}, 宽度: {w}, 通道数: {c})这里最高频率的错误就是把顺序搞反。shape[0]是行数也就是图像高度shape[1]是列数也就是图像宽度。为什么是高在前宽在后因为numpy是数组思维第一个维度永远是行height第二个维度才是列width。记住这一点后面做图像裁剪、遍历像素时就不会写反坐标。3.4 数据类型uint8和float32OpenCV图像默认的数据类型是uint8取值范围0到255。但很多场景下浮点数图像比整型更合适。比如图像归一化时把像素值从0-255映射到0.0-1.0就需要把图像转成float32类型import cv2 img cv2.imread(image.jpg) img_float img.astype(float32) / 255.0深度学习做预处理时如果不做这一步转换直接把uint8的0-255像素值喂给模型最终模型的loss很可能直接崩溃因为数值量级和训练时不匹配。做图像算法开发时要时刻关注数组的dtype这比关注尺寸更重要。3.5 C版的cv::Mat和Python版的numpy如果你以后需要看C的OpenCV代码你会发现里面到处是cv::Mat。Mat是Matrix的缩写本质上是一个矩阵容器里面存了数据指针、行数、列数、通道数和数据类型。Python版里没有单独的Mat类它直接把图像映射成了numpy.ndarray。所以Python中的很多numpy操作比如切片、索引、数学运算都可以直接用在图像上这也是Python版开发效率高的重要原因。明白这层关系后你就能理解为什么上面说OpenCV在底层依赖numpy了——C的Mat负责底层运算Python的numpy则作为它对外暴露的容器接口。4. 图像读写的第一道门imread、imshow、waitKey背后的逻辑4.1 imread()的读取模式和中文路径的坑OpenCV读取图像的函数是cv2.imread()。它有两个参数文件路径和读取模式。常用模式有下面几种模式作用cv2.IMREAD_COLOR默认模式加载为BGR彩色图忽略透明通道cv2.IMREAD_GRAYSCALE加载为灰度图单通道cv2.IMREAD_UNCHANGED加载原始图像包含alpha通道这三种模式对应的数值分别是1、0、-1所以网上很多代码里会直接写cv2.imread(image.jpg, 0)表示读灰度图。两种写法等价看个人习惯。有个细节值得注意cv2.imread()如果读取失败它不会抛异常而是返回None。这意味着如果你路径写错了程序表面上正常运行但后面所有操作都会报NoneType has no attribute shape之类的错误。所以读取后加个判空是好习惯import cv2 img cv2.imread(image.jpg) if img is None: print(图片读取失败检查路径) else: print(f图片尺寸: {img.shape})中文路径问题也特别常见。OpenCV的imread在Windows系统上对中文路径兼容性很差路径里只要有中文返回的就是None。解决办法有两个一是所有图片路径、文件夹命名尽量用英文这在工程上也是好习惯二是用cv2.imdecode配合numpy的fromfile函数来读取可以绕过这个限制import cv2 import numpy as np def imread_chinese(path): data np.fromfile(path, dtypenp.uint8) img cv2.imdecode(data, cv2.IMREAD_COLOR) return img4.2 imshow()和waitKey()为什么窗口会卡住显示图像的API是cv2.imshow()它需要一个窗口名字和一个图像数组import cv2 img cv2.imread(image.jpg) cv2.imshow(My Window, img) cv2.waitKey(0) cv2.destroyAllWindows()这里有个看似奇怪的行为很多初学者只调用了imshow()然后发现窗口显示出来就卡死、程序也停在那里以为OpenCV出了问题。其实不是这是因为imshow()本身只是把图像数据画到窗口里但它不会自动刷新窗口事件。你必须调用cv2.waitKey()窗口才会正常显示并等待键盘输入。waitKey()的参数非常关键。它表示等待键盘输入的时间单位是毫秒。如果参数为0表示无限等待直到用户按下任意键才继续往下走。如果参数给一个正整数比如30就表示最多等待30毫秒不论有没有输入都会返回。这就是视频流处理中为什么每读一帧都要配合waitKey(1)或waitKey(30)——没有这个延时画面根本来不及刷新就闪过去了。热搜里的waitkey为啥没参数时会卡主这个问题答案就在这里没参数或参数为0时程序会一直阻塞住等待按键输入看起来就像卡住了一样。这不是Bug而是OpenCV的事件循环机制。4.3 imwrite()保存图像时能控制哪些质量参数保存图像用cv2.imwrite()它有两个核心参数路径和图像数组。第三个可选参数是编码参数以列表形式传入直接影响保存质量。JPEG格式cv2.IMWRITE_JPEG_QUALITY取值范围0到100默认95。数值越大质量越高文件体积越大。压缩到80左右肉眼基本看不出差别但体积能小一半。PNG格式cv2.IMWRITE_PNG_COMPRESSION取值范围0到9默认3。数值越大压缩率越高保存越慢但这是无损压缩不影响画质。一个实际例子把一张图分别保存为最高质量和最低质量的JPEG看看体积差多少import cv2 img cv2.imread(image.jpg) cv2.imwrite(high_quality.jpg, img, [cv2.IMWRITE_JPEG_QUALITY, 95]) cv2.imwrite(low_quality.jpg, img, [cv2.IMWRITE_JPEG_QUALITY, 30])需要特别注意imwrite()的保存路径目录必须存在否则保存失败而且它同样对中文路径不友好返回值是False而不是抛异常。保存后检查一下返回布尔值能避免后续一堆问题。4.4 destroyAllWindows()和摄像头读取的释放习惯每次用完窗口后我习惯调用cv2.destroyAllWindows()来关闭所有OpenCV创建的窗口。不加也不会报错但在长时间运行时窗口资源没有正常释放可能出现窗口越开越多、内存占用升高的问题。类似的习惯在摄像头读取中更重要。用cv2.VideoCapture()打开摄像头本质是占用了一个硬件资源用完必须调用release()释放否则摄像头会被一直占用下次再打开时可能失败。import cv2 cap cv2.VideoCapture(0) if not cap.isOpened(): print(摄像头打开失败) exit() while True: ret, frame cap.read() if not ret: break cv2.imshow(Camera, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()4.5 OpenCV调用摄像头的原理很多人不理解VideoCapture(0)里的0是什么意思。这个0代表设备的索引号也就是系统识别到的第0个摄像头。如果你的电脑有多个摄像头比如外接USB摄像头和内置摄像头它们会对应不同的索引。读到的frame就是每帧图像本质还是numpy数组所以接下来所有图像处理的操作都能直接作用于它。摄像头的属性比如分辨率、帧率可以用cap.set()设置用cap.get()读取两个参数在使用时需要一个caps_property的编码常量比如CAP_PROP_FRAME_WIDTH和CAP_PROP_FRAME_HEIGHT。后续做实时项目时这套IO流程会反复用到。5. 最常用的图像基础操作缩放、灰度、旋转、裁剪与拼接5.1 resize()缩放的参数选择和插值方式取舍图像缩放是最基础的操作函数是cv2.resize()。它有若干种用法最直观的是直接指定目标尺寸import cv2 img cv2.imread(image.jpg) # 方式一指定目标宽高 resized cv2.resize(img, (400, 300)) # 方式二按比例缩放 resized_by_fx cv2.resize(img, None, fx0.5, fy0.5)注意方式一里(400, 300)的顺序是先宽后高也就是先列后行这和shape返回的高在前宽在后刚好相反非常容易搞反。方式二里用fx和fy指定水平和垂直方向的缩放比例目标尺寸会由源尺寸乘以比例自动得出。resize还有一个关键参数interpolation表示插值方式。默认是cv2.INTER_LINEAR双线性插值大多数场景够用。但当你把图片缩得很小时比如缩小到原来的五分之一双线性插值会出现明显的锯齿和细节丢失。这时候可以用cv2.INTER_AREA它在缩小图像时效果更好能保留更多边缘信息。把图片放大时改用cv2.INTER_CUBIC效果更平滑。# 缩小用面积插值抗锯齿效果更好 small cv2.resize(img, None, fx0.2, fy0.2, interpolationcv2.INTER_AREA) # 放大用三次插值边缘更平滑 big cv2.resize(img, None, fx2.0, fy2.0, interpolationcv2.INTER_CUBIC)插值方式对运行速度也有影响INTER_LINEAR最快INTER_CUBIC最慢。批量处理大量图片时如果对质量要求不高直接用INTER_LINEAR就行能省不少时间。5.2 灰度化和HSVcvtColor颜色空间转换的意义把彩色图转成灰度图是图像处理中最常见的预处理操作之一函数是cv2.cvtColor()import cv2 img cv2.imread(image.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)为什么要转灰度因为灰度图只有一个通道计算量是彩色图的约三分之一。在做人脸检测、边缘检测、轮廓提取这类不依赖颜色的任务时灰度图就足够了还能减少颜色干扰让算法更稳定。颜色空间转换里还有一个非常实用的方向BGR转HSV。HSV分别代表色调Hue、饱和度Saturation、明度Value。它比RGB更符合人对颜色的直观感知。颜色空间适合场景特点RGB/BGR图像存储与显示通道间关联性强光照变化影响大HSV颜色识别与分割色相独立便于按颜色筛选灰度特征检测数据量最小忽略颜色信息做颜色识别时在HSV空间里设定颜色阈值比在RGB里稳定得多。比如识别红色物体在RGB里红色可能受灯光影响从粉色到暗红变化很大但在HSV里只要固定Hue的范围就行。这也是后面颜色识别专题的基础。5.3 旋转图像rotate()和仿射变换的边界区别图像旋转最简单的API是cv2.rotate()它只支持三种固定方向顺时针90度、逆时针90度、180度。import cv2 img cv2.imread(image.jpg) # 逆时针旋转90度 rotated cv2.rotate(img, cv2.ROTATE_90_COUNTERCLOCKWISE) # 顺时针旋转90度 rotated_clockwise cv2.rotate(img, cv2.ROTATE_90_CLOCKWISE) # 旋转180度 rotated_180 cv2.rotate(img, cv2.ROTATE_180)如果要做任意角度的旋转比如旋转45度就得用仿射变换cv2.warpAffine()。它是更通用的几何变换工具不仅支持旋转还支持平移、缩放、错切。核心是先得到变换矩阵再用warpAffine执行变换import cv2 import numpy as np img cv2.imread(image.jpg) h, w img.shape[:2] # 计算旋转矩阵以图像中心为旋转中心逆时针旋转45度缩放比例1.0 M cv2.getRotationMatrix2D((w / 2, h / 2), 45, 1.0) rotated cv2.warpAffine(img, M, (w, h))注意任意角度旋转后图像四个角可能超出画布被直接裁掉了。如果你希望旋转后图像完整保留需要计算旋转后新画布的尺寸并调整变换矩阵中的平移分量。这个细节在以后的图像校正、文档扫描这类项目里非常关键。5.4 裁剪图像numpy切片就够了图像裁剪可能是我用得最多的操作。因为图像的numpy数组结构裁剪在概念上极其简单直接对行列做切片。import cv2 img cv2.imread(image.jpg) # 裁剪出左上角200x200的区域 # 注意第一个维度是行高第二个维度是列宽 cropped img[0:200, 0:200]这里最容易犯的错误就是维度顺序。很多人想裁左上角会写img[0:200, 0:200]说成是宽200高200其实它表达的是第0到200行、第0到200列。如果你要裁一个宽300高200的区域需要写成img[0:200, 0:300]行数在前对应高度列数在后对应宽度。裁剪尤其在抠出感兴趣区域ROI时常用。比如人脸检测后检测结果返回了人脸的边界框坐标接下来用切片就能把所有人脸区域批量裁出来存成数据集。5.5 拼接图像np.hstack和cv2.hconcat怎么选把多张图拼在一起显示是调试时的刚需。比如你想把原图、灰度图、边缘检测结果三张图放在同一个窗口里对比。最简单的拼法是numpy的拼接函数import cv2 import numpy as np img1 cv2.imread(image1.jpg) img2 cv2.imread(image2.jpg) # 横向拼接 hstack np.hstack((img1, img2)) # 纵向拼接 vstack np.vstack((img1, img2))np.hstack的硬性要求是两张图的高度必须一致np.vstack要求宽度一致否则会直接报错。这在批处理里可能不太方便因为读进来的图片尺寸常常不一样。OpenCV提供了更灵活的函数cv2.hconcat()和cv2.vconcat()当尺寸不一致时需要先统一处理。实际调试中我习惯写一个简单函数把要对比的图像先缩放成统一尺寸再拼接这样不管原图多大都能整齐地显示在一起import cv2 def concat_images(imgs, size(400, 300)): # 将所有图像缩放到统一尺寸 resized [cv2.resize(im, size) for im in imgs] # 横向拼接 return cv2.hconcat(resized) img1 cv2.imread(image1.jpg) img2 cv2.imread(image2.jpg) result concat_images([img1, img2]) cv2.imshow(Comparison, result) cv2.waitKey(0)拼接的实际价值在项目展示和调试阶段特别明显。一次跑完算法把所有中间结果全部显示出来比单独一张张看直观得多也能更早地发现问题。6. 实战组合拳从零写一个亮度检测小工具聊完基础操作我用一个完整的例子把这些知识点串起来。这个例子的场景是批量检测一批图片的亮度是否合格自动把过曝和欠曝的图片标记出来。这是我在工业项目里常碰到的需求比如产品外观检测前先剔除曝光不合格的图像避免影响后续算法。6.1 需求拆解和方案设计实现思路非常直接遍历文件夹中所有图片。对每张图片转为灰度计算全局平均亮度。设定一个合理亮度区间比如80到190。平均亮度高于190判定为过曝低于80判定为欠曝其余正常。将判定结果打印出来并将异常的图片单独保存到对应文件夹。平均亮度的计算用cv2.mean()函数它返回每个通道的均值。因为灰度图是单通道取第一个值就行。这个方案不算复杂但它涵盖了imread、cvtColor、resize、imwrite、路径处理、批量遍历这批核心操作非常适合用来巩固本章内容。6.2 完整代码与逐行解析import cv2 import os import numpy as np def classify_brightness(img, low_thresh80, high_thresh190): # 转为灰度图便于计算亮度 gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 计算平均亮度均值范围在0-255之间 mean_val cv2.mean(gray)[0] if mean_val high_thresh: return over, mean_val elif mean_val low_thresh: return under, mean_val else: return normal, mean_val def batch_check(input_dir, output_dir): # 创建输出目录 for sub in [over, under, normal]: os.makedirs(os.path.join(output_dir, sub), exist_okTrue) total 0 abnormal 0 for filename in os.listdir(input_dir): if not filename.lower().endswith((.jpg, .jpeg, .png)): continue filepath os.path.join(input_dir, filename) img cv2.imread(filepath) # 读取失败的文件直接跳过不中断程序 if img is None: print(f读取失败跳过: {filepath}) continue label, mean_val classify_brightness(img) total 1 # 输出到控制台方便实时观察 print(f{filename}: 平均亮度 {mean_val:.1f} - {label}) if label ! normal: abnormal 1 # 异常图片单独保存到对应文件夹 if label in [over, under]: save_dir os.path.join(output_dir, label) save_path os.path.join(save_dir, filename) cv2.imwrite(save_path, img) print(f\n检查完成: 共 {total} 张, 正常 {total - abnormal} 张, 异常 {abnormal} 张) if __name__ __main__: batch_check(./test_images, ./result)几个细节值得说明。文件扩展名判断用lower()统一转小写避免大写后缀漏检。读图返回None时continue而不是直接报错保证一个坏文件不影响整批任务。输出目录用os.makedirs的exist_okTrue参数重复运行不会报错。这些写法和习惯在真实项目中开发效率高很多。6.3 实测效果和性能注意点实测一组包含150张图片的测试集代码运行时间不到1秒所有图片的平均亮度统计、分类、异常图片的保存一次完成。这个速度对批量处理来说是足够的。如果你有成千上万张图片需要处理可以考虑用multiprocessing做多进程并行因为在图像IO和计算密集场景下多进程能明显提升吞吐量。但那是工程优化层面的事等基础玩熟了再接触也不迟。6.4 从这里还可以往哪些方向走这个亮度检测工具已经很贴近实际项目了但离生产环境还有一段距离。你可以尝试在它基础上加入更丰富的功能比如增加直方图统计、对异常图片自动裁剪保存ROI、配合摄像头做实时亮度监控。再往后走就会碰到人脸检测、轮廓提取、颜色识别、目标跟踪这些更有意思的方向。后续我计划在系列文章里逐一展开包括边缘检测、形态学操作、轮廓提取与拟合以及基于深度学习的检测方法。我在实际做完这个小工具后最大的感受是OpenCV的入门知识密度并不高难点在于把这些零散的API组合起来解决一个真实问题。当你能够不看文档独立把读图、预处理、处理、保存这个流程写顺说明基础已经过关了。接下来你会发现OpenCV的绝大部分高级功能都是在这套基本功上叠加出来的。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门