第一次接触OpenCV时,我也被它强大的功能和复杂的文档吓到过。但后来发现,只要掌握几个核心函数,就能完成90%的日常图像处理任务。这里分享我的学习路径,帮你避开那些新手常踩的坑。
安装OpenCV其实很简单,用pip一行命令就能搞定:
python复制pip install opencv-python
但很多人不知道还要安装扩展模块:
python复制pip install opencv-contrib-python
这个包包含了SIFT、SURF等高级算法。我建议直接用这个,免得后面用到时又要重新安装。
读取和显示图像是最基础的操作:
python复制import cv2
img = cv2.imread('image.jpg') # 读取图像
cv2.imshow('window', img) # 显示图像
cv2.waitKey(0) # 等待按键
这里有个坑:OpenCV默认读取的是BGR格式,而matplotlib显示用的是RGB格式。如果直接用plt显示OpenCV读取的图像,颜色会不正常。解决方法很简单:
python复制img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
图像的基本属性获取也很重要:
python复制print(img.shape) # (高度, 宽度, 通道数)
print(img.dtype) # 数据类型
print(img.size) # 总像素数
这些在后续处理中经常用到,比如做resize时需要知道原始尺寸。
图像的几何变换是项目中最常用的功能之一。平移、旋转、缩放这些操作看着简单,但实际使用时有很多细节要注意。
平移变换的代码示例:
python复制import numpy as np
height, width = img.shape[:2]
M = np.float32([[1, 0, 100], [0, 1, 50]]) # 向右平移100,向下平移50
dst = cv2.warpAffine(img, M, (width, height))
这里容易忽略第三个参数是输出图像尺寸,如果设置小了图像会被裁剪。
旋转就更复杂一些,需要先计算变换矩阵:
python复制M = cv2.getRotationMatrix2D((width/2, height/2), 45, 1) # 中心点,角度,缩放因子
dst = cv2.warpAffine(img, M, (width, height))
我遇到过旋转后图像被裁剪的问题,解决方法是通过计算新边界尺寸:
python复制def rotate_image(image, angle):
(h, w) = image.shape[:2]
(cX, cY) = (w // 2, h // 2)
M = cv2.getRotationMatrix2D((cX, cY), angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
return cv2.warpAffine(image, M, (nW, nH))
图像滤波是去除噪声、增强特征的关键步骤。常用的滤波方法有:
python复制blur = cv2.blur(img, (5,5))
python复制blur = cv2.GaussianBlur(img, (5,5), 0)
python复制median = cv2.medianBlur(img, 5)
python复制blur = cv2.bilateralFilter(img, 9, 75, 75)
在实际项目中,我通常先用中值滤波去除明显的噪声点,再用双边滤波平滑图像同时保留边缘。对于医疗影像这类对细节要求高的场景,还会配合非局部均值滤波:
python复制dst = cv2.fastNlMeansDenoisingColored(img, None, 10, 10, 7, 21)
边缘检测是很多计算机视觉任务的基础。最常用的是Canny边缘检测:
python复制edges = cv2.Canny(img, 100, 200)
这两个阈值很关键,太低会有太多噪声,太高会丢失重要边缘。我的经验是先设高阈值,再逐步调低直到满意。
找到边缘后,通常需要提取轮廓:
python复制contours, hierarchy = cv2.findContours(edges, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
这里有个大坑:不同OpenCV版本返回的参数个数不同!在OpenCV 3和4中,返回的是两个值,而在OpenCV 2中是三个值。
绘制轮廓时,可以只画外轮廓:
python复制cv2.drawContours(img, contours, -1, (0,255,0), 2)
或者分析每个轮廓的属性:
python复制for cnt in contours:
area = cv2.contourArea(cnt)
if area > 500: # 过滤小轮廓
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)
特征提取是目标识别、图像拼接等高级应用的基础。ORB是常用的特征点检测算法:
python复制orb = cv2.ORB_create()
keypoints, descriptors = orb.detectAndCompute(img, None)
img_kp = cv2.drawKeypoints(img, keypoints, None, color=(0,255,0))
特征匹配的完整流程:
python复制# 初始化ORB检测器
orb = cv2.ORB_create()
# 查找关键点和描述符
kp1, des1 = orb.detectAndCompute(img1, None)
kp2, des2 = orb.detectAndCompute(img2, None)
# 创建BFMatcher对象
bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
# 匹配描述符
matches = bf.match(des1, des2)
# 按距离排序
matches = sorted(matches, key=lambda x:x.distance)
# 绘制前50个匹配点
img3 = cv2.drawMatches(img1, kp1, img2, kp2, matches[:50], None, flags=2)
在实际项目中,我还会用RANSAC算法去除误匹配:
python复制# 转换关键点为数组格式
src_pts = np.float32([kp1[m.queryIdx].pt for m in matches]).reshape(-1,1,2)
dst_pts = np.float32([kp2[m.trainIdx].pt for m in matches]).reshape(-1,1,2)
# 使用RANSAC计算单应性矩阵
M, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
# 只保留内点
matches = [m for i,m in enumerate(matches) if mask[i]]
用OpenCV实现手机文档扫描功能是个很好的练手项目。主要步骤包括:
python复制gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5,5), 0)
edged = cv2.Canny(gray, 75, 200)
python复制cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02*peri, True)
if len(approx) == 4:
screenCnt = approx
break
python复制def four_point_transform(image, pts):
rect = order_points(pts)
(tl, tr, br, bl) = rect
widthA = np.sqrt(((br[0]-bl[0])**2)+((br[1]-bl[1])**2))
widthB = np.sqrt(((tr[0]-tl[0])**2)+((tr[1]-tl[1])**2))
maxWidth = max(int(widthA), int(widthB))
heightA = np.sqrt(((tr[0]-br[0])**2)+((tr[1]-br[1])**2))
heightB = np.sqrt(((tl[0]-bl[0])**2)+((tl[1]-bl[1])**2))
maxHeight = max(int(heightA), int(heightB))
dst = np.array([
[0,0],
[maxWidth-1,0],
[maxWidth-1,maxHeight-1],
[0,maxHeight-1]], dtype="float32")
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
return warped
结合摄像头采集和图像处理,可以实现实时人脸马赛克效果:
python复制face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml')
python复制cap = cv2.VideoCapture(0)
while True:
ret, frame = cap.read()
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
for (x,y,w,h) in faces:
# 对人脸区域进行马赛克处理
roi = frame[y:y+h, x:x+w]
roi = cv2.resize(roi, (10,10), interpolation=cv2.INTER_LINEAR)
roi = cv2.resize(roi, (w,h), interpolation=cv2.INTER_NEAREST)
frame[y:y+h, x:x+w] = roi
cv2.imshow('frame', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
这个例子展示了OpenCV在实时视频处理中的应用。在实际项目中,我还会加入人脸识别功能,只对特定人脸打马赛克。