Python+OpenCV实战:5个图像处理小项目带你轻松入门计算机视觉

计算机视觉正以前所未有的速度渗透到我们生活的方方面面——从手机相册的智能分类到自动驾驶的环境感知,从工业质检的精准识别到医疗影像的辅助诊断。对于初学者而言,掌握这项技术的捷径不是死记硬背理论公式,而是通过动手实践来理解核心概念。本文将带你用Python和OpenCV完成五个循序渐进的实战项目,每个项目都聚焦解决一个具体的视觉问题。

1. 环境配置与基础图像操作

在开始项目前,需要搭建开发环境。推荐使用Anaconda创建独立的Python环境,避免库版本冲突:

conda create -n cv_env python=3.8
conda activate cv_env
pip install opencv-python numpy matplotlib

验证安装是否成功:

import cv2
print(cv2.__version__)  # 应输出4.x版本

图像基础操作三板斧

  • 读取图像:img = cv2.imread('image.jpg')
  • 显示图像:cv2.imshow('Window', img)
  • 保存图像:cv2.imwrite('output.jpg', img)

注意:OpenCV默认使用BGR色彩空间而非RGB,使用matplotlib显示时需先转换:

rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

图像本质上是多维数组,通过NumPy可以高效操作:

# 获取像素值
pixel = img[100, 200]  # (B,G,R)值

# 区域裁剪
roi = img[50:150, 100:300]

# 颜色通道分离
b, g, r = cv2.split(img)

2. 项目一:智能照片滤镜生成器

这个项目将实现包括复古、素描、卡通等多种风格的实时滤镜效果。核心原理是通过卷积核实现图像滤波:

def apply_filter(img, filter_type):
    if filter_type == "blur":
        return cv2.GaussianBlur(img, (15,15), 0)
    elif filter_type == "sketch":
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        inv_gray = 255 - gray
        blur = cv2.GaussianBlur(inv_gray, (21,21), 0)
        return cv2.divide(gray, 255-blur, scale=256)
    elif filter_type == "cartoon":
        # 边缘保留滤波
        smooth = cv2.bilateralFilter(img, 9, 75, 75)
        # 边缘增强
        edges = cv2.Canny(img, 100, 200)
        edges = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
        return cv2.bitwise_and(smooth, edges)

效果对比表

滤镜类型 核心方法 适用场景
高斯模糊 GaussianBlur 背景虚化
铅笔画 反色+高斯模糊 艺术效果
卡通化 双边滤波+Canny边缘 人像处理

实时摄像头滤镜实现:

cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    filtered = apply_filter(frame, "cartoon")
    cv2.imshow('Live Filter', filtered)
    if cv2.waitKey(1) == ord('q'):
        break
cap.release()

3. 项目二:人脸检测与特征标记

OpenCV内置的Haar级联分类器能快速实现人脸检测。首先下载预训练模型:

face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')

检测与标记代码:

def detect_faces(img):
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3, 5)
    
    for (x,y,w,h) in faces:
        cv2.rectangle(img, (x,y), (x+w,y+h), (255,0,0), 2)
        roi_gray = gray[y:y+h, x:x+w]
        roi_color = img[y:y+h, x:x+w]
        
        eyes = eye_cascade.detectMultiScale(roi_gray)
        for (ex,ey,ew,eh) in eyes:
            cv2.rectangle(roi_color, (ex,ey), (ex+ew,ey+eh), (0,255,0), 2)
    
    return img

性能优化技巧

  • 缩小检测图像尺寸可提速但会降低精度
  • scaleFactor参数控制图像金字塔缩放比例(建议1.01-1.5)
  • minNeighbors参数决定检测框合并阈值(越高误检越少)

更精准的DNN人脸检测:

net = cv2.dnn.readNetFromCaffe("deploy.prototxt", "res10_300x300_ssd_iter_140000.caffemodel")
blob = cv2.dnn.blobFromImage(cv2.resize(img, (300,300)), 1.0, (300,300), (104.0,177.0,123.0))
net.setInput(blob)
detections = net.forward()

4. 项目三:文档扫描与矫正

这个项目将通过图像处理实现手机拍摄文档的自动矫正,流程包括:

  1. 边缘检测
  2. 轮廓查找
  3. 透视变换
def scan_document(img):
    # 预处理
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    blur = cv2.GaussianBlur(gray, (5,5), 0)
    edged = cv2.Canny(blur, 75, 200)
    
    # 查找轮廓
    contours, _ = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
    contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
    
    # 寻找矩形轮廓
    for c in contours:
        peri = cv2.arcLength(c, True)
        approx = cv2.approxPolyDP(c, 0.02*peri, True)
        if len(approx) == 4:
            doc_cnt = approx
            break
    
    # 透视变换
    warped = four_point_transform(img, doc_cnt.reshape(4,2))
    return warped

def four_point_transform(img, 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(img, M, (maxWidth, maxHeight))
    return warped

常见问题解决方案

  • 光线不均:使用自适应阈值代替Canny
  • 复杂背景:先进行颜色分割或形态学操作
  • 曲面文档:需要更复杂的网格变换算法

5. 项目四:实时运动检测系统

基于背景减除实现运动物体检测:

fgbg = cv2.createBackgroundSubtractorMOG2(history=500, varThreshold=16, detectShadows=False)

while True:
    ret, frame = cap.read()
    fgmask = fgbg.apply(frame)
    
    # 后处理
    kernel = np.ones((5,5), np.uint8)
    fgmask = cv2.morphologyEx(fgmask, cv2.MORPH_OPEN, kernel)
    
    # 查找轮廓
    contours, _ = cv2.findContours(fgmask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for c in contours:
        if cv2.contourArea(c) < 500:
            continue
        (x,y,w,h) = cv2.boundingRect(c)
        cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 2)
    
    cv2.imshow('Motion Detection', frame)
    if cv2.waitKey(1) == ord('q'):
        break

背景建模算法对比

算法类型 优点 缺点 适用场景
MOG 计算量小 对光照敏感 静态背景
MOG2 自适应背景 阴影检测不准 动态背景
GMG 初始化快 需要学习帧 固定摄像头

6. 项目五:全景图像拼接

实现多张重叠照片的自动拼接:

def stitch_images(images):
    stitcher = cv2.Stitcher_create()
    status, panorama = stitcher.stitch(images)
    
    if status == cv2.Stitcher_OK:
        # 裁剪黑色边界
        panorama = cv2.copyMakeBorder(panorama, 10,10,10,10, cv2.BORDER_CONSTANT, (0,0,0))
        gray = cv2.cvtColor(panorama, cv2.COLOR_BGR2GRAY)
        _, thresh = cv2.threshold(gray, 1, 255, cv2.THRESH_BINARY)
        contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        x,y,w,h = cv2.boundingRect(contours[0])
        panorama = panorama[y:y+h, x:x+w]
        return panorama
    else:
        print("拼接失败 (错误码: %d)" % status)
        return None

拍摄技巧

  • 保持30%-40%的图像重叠区域
  • 使用固定曝光和白平衡
  • 沿同一水平线平移相机
  • 避免近景物体移动

对于更精细的控制,可以手动实现特征点匹配:

def manual_stitch(img1, img2):
    # 特征检测
    orb = cv2.ORB_create()
    kp1, des1 = orb.detectAndCompute(img1, None)
    kp2, des2 = orb.detectAndCompute(img2, None)
    
    # 特征匹配
    bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
    matches = bf.match(des1, des2)
    matches = sorted(matches, key=lambda x:x.distance)
    
    # 计算单应性矩阵
    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)
    H, _ = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, 5.0)
    
    # 透视变换
    h1,w1 = img1.shape[:2]
    h2,w2 = img2.shape[:2]
    result = cv2.warpPerspective(img1, H, (w1+w2, h1))
    result[0:h2, 0:w2] = img2
    return result

更多推荐