计算机视觉是Python应用最广泛的领域之一,而OpenCV则是这个领域里绕不开的工具库。它提供了上千个优化好的图像处理函数,从最基础的像素读写到复杂的目标检测都能覆盖。这篇文章会从零开始,带你理解OpenCV的核心概念,并通过几个典型实战案例掌握图像识别的基本套路。

一、环境搭建与图像基础操作
先说环境。推荐直接用pip安装,注意安装的包名是opencv-python而不是opencv:
pip install opencv-python numpy matplotlib
装好之后,第一步是理解OpenCV中图像的本质。在OpenCV的世界里,一张图就是一个三维的NumPy数组,形状是(高度, 宽度, 通道数)。彩色图是BGR三个通道(注意不是RGB,这是OpenCV的历史遗留问题),灰度图则是二维数组。理解了这一点,你对图像的所有操作本质上都是在操作NumPy数组。
下面是最基础的读图、显示和保存操作:
import cv2
import numpy as np
# 读取图像,第二个参数常用三种取值
img = cv2.imread('test.jpg', cv2.IMREAD_COLOR) # 彩色读取,默认值
# cv2.IMREAD_GRAYSCALE 灰度读取
# cv2.IMREAD_UNCHANGED 含透明通道读取
print(img.shape) # 输出 (高, 宽, 3)
cv2.imshow('window', img) # 显示图像
cv2.waitKey(0) # 等待按键,0表示无限等待
cv2.destroyAllWindows() # 关闭所有窗口
cv2.imwrite('output.jpg', img) # 保存图像有个常见的坑要提醒一下:如果路径包含中文,cv2.imread会静默失败,返回None,而且不报任何错误。很多初学者代码写对了却看不到图像,十有八九是路径问题。解决办法是用cv2.imdecode配合np.fromfile读取,或者干脆保证路径全为英文。
像素级的操作也很直观。想修改某个像素点的颜色,直接按数组索引赋值即可,比如img[100, 50] = (255, 0, 0)会把坐标(100, 50)处的像素设为蓝色。想裁剪图像,用NumPy的切片语法:ROI = img[50:200, 100:300]就能截取一块感兴趣区域。这些基础操作看似简单,却是后续一切视觉任务的根基。
二、图像预处理:滤波、阈值与边缘检测
真实场景中的图片往往有噪点、光照不均等问题,直接识别效果会很差,所以预处理是图像识别流水线中最关键的一环。预处理做得好,后面简单的算法也能出效果;做得不好,再高级的模型也救不回来。
首先是降噪。最常用的是高斯模糊,它的原理是对图像中每个像素用周围像素的加权平均来替代,权重服从二维高斯分布,离中心越近权重越大。这样孤立的噪点会被周围像素稀释掉,而图像的整体轮廓得以保留。除了高斯模糊,中值滤波对椒盐噪声(图像上随机出现的黑白点)效果特别好,因为它取的是邻域中位数,极端值直接被忽略。
import cv2
img = cv2.imread('test.jpg')
# 高斯模糊:(5, 5)是卷积核大小,必须是奇数;0表示标准差自动计算
blur = cv2.GaussianBlur(img, (5, 5), 0)
# 中值滤波:对椒盐噪声效果好
median = cv2.medianBlur(img, 5)
cv2.imshow('blur', blur)
cv2.imshow('median', median)
cv2.waitKey(0)然后是灰度转换和二值化。大部分识别算法并不需要彩色信息,转成灰度图能大幅减少计算量。二值化则把灰度图进一步简化成纯黑白,常用的是全局阈值法和自适应阈值法。当图像光照均匀时用cv2.threshold就够了,但拍摄场景光照不均(比如一侧亮一侧暗)时,自适应阈值法会对每个小区域单独计算阈值,效果明显更好。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 全局阈值:127为阈值,255为超过阈值后设置的值
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 自适应阈值:每个像素的阈值由周围11x11区域的均值减去C决定
adaptive = cv2.adaptiveThreshold(gray, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)最后是边缘检测,这里重点讲Canny算子。它是经典的边缘检测算法,分为四步:先用高斯滤波降噪,再计算每个像素的梯度强度和方向,接着做非极大值抑制(把粗边缘细化成一个像素宽),最后用双阈值机制决定哪些边缘是真正的边缘、哪些可以忽略。使用时只需要调两个阈值参数:
edges = cv2.Canny(gray, threshold1=100, threshold2=200) # 经验法则:低阈值取高阈值的1/2到1/3 # 边缘太少就降低阈值,噪声太多就升高阈值
三、轮廓检测与几何分析
有了二值化或边缘检测的结果,就可以提取轮廓了。轮廓可以理解为图像中相同颜色或亮度的连通区域的边界线,它是做物体计数、形状识别、尺寸测量的基础。cv2.findContours函数会返回所有检测到的轮廓点集,配合cv2.drawContours可以把它们画出来。
import cv2
img = cv2.imread('shapes.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# RETR_EXTERNAL只检测外轮廓,CHAIN_APPROX_SIMPLE压缩轮廓点节省内存
contours, hierarchy = cv2.findContours(binary,
cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (0, 255, 0), 2) # -1表示画所有轮廓
cv2.imshow('contours', img)
cv2.waitKey(0)拿到轮廓之后,几何分析才真正开始。常用的函数有几个:cv2.contourArea计算轮廓面积,可以用来过滤掉太小的噪声区域;cv2.arcLength计算周长;cv2.boundingRect返回轮廓的外接矩形,适合画检测框;cv2.minAreaRect返回最小面积旋转矩形,处理倾斜的物体更准确。
形状识别也是轮廓分析的经典应用。比如要判断一个轮廓是三角形、矩形还是圆形,可以用cv2.approxPolyDP对轮廓做多边形拟合,拟合出来的顶点数是3就是三角形,是4就是四边形。判断圆则可以比较轮廓面积和外接圆面积的接近程度,越接近说明越圆。这套思路在工业检测中应用非常多,比如零件外观检查、计数分拣等场景。
# 多边形拟合:0.02 * 周长 是拟合精度,越小拟合越精细
for cnt in contours:
area = cv2.contourArea(cnt)
if area < 100: # 过滤小噪声
continue
approx = cv2.approxPolyDP(cnt, 0.02 * cv2.arcLength(cnt, True), True)
vertices = len(approx)
if vertices == 3:
shape = 'triangle'
elif vertices == 4:
shape = 'rectangle'
else:
shape = 'circle or polygon'四、实战案例:人脸检测
掌握了前面的基础,就可以做真正的识别任务了。人脸检测最经典的方案是Haar级联分类器,OpenCV自带训练好的模型文件,位于安装目录的data文件夹下。它的原理是利用Haar特征(比如眼睛区域比脸颊暗、鼻梁比两侧亮这类明暗对比模式),通过数千个弱分类器级联投票来判断一个区域是否为人脸。级联的结构让非人脸区域在前几级就被快速排除,所以速度非常快,在普通CPU上就能实时运行。
import cv2
# 加载官方预训练的人脸检测模型
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
img = cv2.imread('people.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# detectMultiScale参数说明:
# scaleFactor=1.1 图像金字塔缩放步长,越小检测越准但越慢
# minNeighbors=5 每个候选区域至少要被检测到几次才算有效,越大误检越少
# minSize=(30, 30) 忽略过小的区域
faces = face_cascade.detectMultiScale(gray, 1.1, 5, minSize=(30, 30))
for (x, y, w, h) in faces:
cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('faces', img)
cv2.waitKey(0)Haar方案的优点是零成本开箱即用,缺点是对侧脸、遮挡、暗光比较敏感。如果需要更高的准确率,可以用OpenCV自带的DNN模块加载深度学习模型,比如YOLO或者SSD,检测效果会好一个档次,而且OpenCV的DNN模块只做推理,不依赖PyTorch或TensorFlow,部署非常轻量。此外也可以试试MediaPipe这个库,人脸检测加关键点定位一行代码就能搞定,很适合做实时应用。
到这里,一条完整的图像识别链路就打通了:读图、预处理、特征提取、检测输出。建议学习时不要只抄代码,每跑一个函数就改动参数观察输出的变化,比如把Canny的两个阈值调大调小看看边缘数量怎么变,这种手感是看教程看不来的。计算机视觉的入门门槛其实不高,难的是把基础知识组合起来解决实际问题,多找一些真实场景的小项目练手,比如车牌识别、文档扫描、手势控制,进步会比刷理论快得多。
Python计算机视觉OpenCV图像识别修改时间:2026-09-05 06:20:48