【OpenCV 入门到精通 11】机器学习应用:KNN、SVM 与 K-Means 实战

【OpenCV 入门到精通 11】机器学习应用:KNN、SVM 与 K-Means 实战

标签 :OpenCV KNN SVM K-Means 机器学习 OCR

难度 :⭐⭐⭐⭐ | 阅读时长 :约 60 分钟 | 系列 :第 11/12 集

前置 :10 视频分析 | 下一集 :12 综合实战


写在前面

深度学习之前,传统机器学习 是 CV 的主流方案。OpenCV 内置了 KNN、SVM、K-Means 等经典算法,至今仍在 OCR、简单分类等场景有用。本集对应官方 第8章 机器学习。


本集学习目标

  • 使用 KNN 进行手写数字识别
  • 使用 SVM 进行分类
  • 使用 K-Means 进行图像分割
  • 理解传统 ML 与深度学习的适用边界

文章目录

  • [【OpenCV 入门到精通 11】机器学习应用:KNN、SVM 与 K-Means 实战](#【OpenCV 入门到精通 11】机器学习应用:KNN、SVM 与 K-Means 实战)
    • 写在前面
    • 本集学习目标
    • [11.1 OpenCV ML 模块](#11.1 OpenCV ML 模块)
    • [11.2 KNN 手写数字识别](#11.2 KNN 手写数字识别)
      • [简化版:用 sklearn 数据](#简化版:用 sklearn 数据)
    • [11.3 SVM 分类](#11.3 SVM 分类)
      • [SVM 核函数](#SVM 核函数)
    • [11.4 K-Means 聚类](#11.4 K-Means 聚类)
    • [11.5 传统 ML vs 深度学习](#11.5 传统 ML vs 深度学习)
    • [11.6 踩坑指南](#11.6 踩坑指南)
    • [11.7 本集小结](#11.7 本集小结)

11.1 OpenCV ML 模块

算法 类 用途
KNN cv2.ml.KNearest_create() 最近邻分类
SVM cv2.ml.SVM_create() 支持向量机
决策树 cv2.ml.DTrees_create() 决策树
随机森林 cv2.ml.RTrees_create() 集成学习
贝叶斯 cv2.ml.NormalBayesClassifier_create() 朴素贝叶斯
K-Means cv2.kmeans() 聚类

通用流程

#mermaid-svg-Ks1HSrQfksnZtHak{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Ks1HSrQfksnZtHak .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Ks1HSrQfksnZtHak .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Ks1HSrQfksnZtHak .error-icon{fill:#552222;}#mermaid-svg-Ks1HSrQfksnZtHak .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Ks1HSrQfksnZtHak .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Ks1HSrQfksnZtHak .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Ks1HSrQfksnZtHak .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Ks1HSrQfksnZtHak .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Ks1HSrQfksnZtHak .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Ks1HSrQfksnZtHak .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Ks1HSrQfksnZtHak .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Ks1HSrQfksnZtHak .marker.cross{stroke:#333333;}#mermaid-svg-Ks1HSrQfksnZtHak svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Ks1HSrQfksnZtHak p{margin:0;}#mermaid-svg-Ks1HSrQfksnZtHak .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Ks1HSrQfksnZtHak .cluster-label text{fill:#333;}#mermaid-svg-Ks1HSrQfksnZtHak .cluster-label span{color:#333;}#mermaid-svg-Ks1HSrQfksnZtHak .cluster-label span p{background-color:transparent;}#mermaid-svg-Ks1HSrQfksnZtHak .label text,#mermaid-svg-Ks1HSrQfksnZtHak span{fill:#333;color:#333;}#mermaid-svg-Ks1HSrQfksnZtHak .node rect,#mermaid-svg-Ks1HSrQfksnZtHak .node circle,#mermaid-svg-Ks1HSrQfksnZtHak .node ellipse,#mermaid-svg-Ks1HSrQfksnZtHak .node polygon,#mermaid-svg-Ks1HSrQfksnZtHak .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Ks1HSrQfksnZtHak .rough-node .label text,#mermaid-svg-Ks1HSrQfksnZtHak .node .label text,#mermaid-svg-Ks1HSrQfksnZtHak .image-shape .label,#mermaid-svg-Ks1HSrQfksnZtHak .icon-shape .label{text-anchor:middle;}#mermaid-svg-Ks1HSrQfksnZtHak .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Ks1HSrQfksnZtHak .rough-node .label,#mermaid-svg-Ks1HSrQfksnZtHak .node .label,#mermaid-svg-Ks1HSrQfksnZtHak .image-shape .label,#mermaid-svg-Ks1HSrQfksnZtHak .icon-shape .label{text-align:center;}#mermaid-svg-Ks1HSrQfksnZtHak .node.clickable{cursor:pointer;}#mermaid-svg-Ks1HSrQfksnZtHak .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Ks1HSrQfksnZtHak .arrowheadPath{fill:#333333;}#mermaid-svg-Ks1HSrQfksnZtHak .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Ks1HSrQfksnZtHak .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Ks1HSrQfksnZtHak .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ks1HSrQfksnZtHak .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Ks1HSrQfksnZtHak .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ks1HSrQfksnZtHak .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Ks1HSrQfksnZtHak .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Ks1HSrQfksnZtHak .cluster text{fill:#333;}#mermaid-svg-Ks1HSrQfksnZtHak .cluster span{color:#333;}#mermaid-svg-Ks1HSrQfksnZtHak div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Ks1HSrQfksnZtHak .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Ks1HSrQfksnZtHak rect.text{fill:none;stroke-width:0;}#mermaid-svg-Ks1HSrQfksnZtHak .icon-shape,#mermaid-svg-Ks1HSrQfksnZtHak .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Ks1HSrQfksnZtHak .icon-shape p,#mermaid-svg-Ks1HSrQfksnZtHak .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Ks1HSrQfksnZtHak .icon-shape .label rect,#mermaid-svg-Ks1HSrQfksnZtHak .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Ks1HSrQfksnZtHak .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Ks1HSrQfksnZtHak .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Ks1HSrQfksnZtHak :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 准备数据
特征提取
训练模型
预测
评估


11.2 KNN 手写数字识别

python 复制代码
import cv2
import numpy as np

# 加载 OpenCV 自带的手写数字样本
samples = np.loadtxt("digits.png", np.float32)
samples = samples.reshape(-1, 20, 20)  # 5000 个 20x20 图像

# 准备训练数据
train_data = samples.reshape(-1, 400).astype(np.float32)
labels = np.repeat(np.arange(10), 250)[:, np.newaxis].astype(np.float32)

knn = cv2.ml.KNearest_create()
knn.train(train_data, cv2.ml.ROW_SAMPLE, labels)

# 预测
test_sample = samples[1023].reshape(1, 400).astype(np.float32)
ret, result, neighbours, dist = knn.findNearest(test_sample, k=5)
print(f"预测: {int(result[0][0])}")

简化版:用 sklearn 数据

python 复制代码
from sklearn.datasets import load_digits
import cv2
import numpy as np

digits = load_digits()
X = digits.data.astype(np.float32)
y = digits.target.astype(np.float32)

# 划分训练/测试
split = int(0.8 * len(X))
knn = cv2.ml.KNearest_create()
knn.train(X[:split], cv2.ml.ROW_SAMPLE, y[:split])

correct = 0
for i in range(split, len(X)):
    _, result, _, _ = knn.findNearest(X[i:i+1], k=5)
    if int(result[0][0]) == int(y[i]):
        correct += 1
print(f"准确率: {correct/(len(X)-split)*100:.1f}%")

11.3 SVM 分类

python 复制代码
import cv2
import numpy as np

# 生成简单二分类数据
np.random.seed(42)
data1 = np.random.randn(50, 2).astype(np.float32) + np.array([2, 2])
data2 = np.random.randn(50, 2).astype(np.float32) + np.array([-2, -2])
train_data = np.vstack([data1, data2])
labels = np.array([1]*50 + [-1]*50, dtype=np.float32)

svm = cv2.ml.SVM_create()
svm.setType(cv2.ml.SVM_C_SVC)
svm.setKernel(cv2.ml.SVM_LINEAR)
svm.train(train_data, cv2.ml.ROW_SAMPLE, labels)

# 预测
test = np.array([[0, 0]], dtype=np.float32)
_, result = svm.predict(test)
print(f"预测类别: {int(result[0][0])}")

SVM 核函数

核 适用
SVM_LINEAR 线性可分
SVM_RBF 非线性(最常用)
SVM_POLY 多项式
SVM_SIGMOID S 形

11.4 K-Means 聚类

将图像像素按颜色聚类,实现简单分割:

python 复制代码
import cv2
import numpy as np

img = cv2.imread("photo.jpg")
data = img.reshape((-1, 3)).astype(np.float32)

K = 3  # 聚类数
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)
_, labels, centers = cv2.kmeans(data, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)

centers = np.uint8(centers)
segmented = centers[labels.flatten()].reshape(img.shape)

cv2.imshow("K-Means", segmented)
cv2.waitKey(0)
参数 说明
K 聚类数量
criteria 终止条件
attempts 不同初始化的尝试次数

11.5 传统 ML vs 深度学习

维度 传统 ML (KNN/SVM) 深度学习 (YOLO/CNN)
特征 需手工设计 自动学习
数据量 小数据可用 需要大量数据
速度 快 较慢(GPU 加速)
精度 简单任务够用 复杂任务更强
可解释性 较好 较差

💡 选择建议:OCR 简单场景、颜色聚类 → 传统 ML;目标检测、复杂分类 → 深度学习。


11.6 踩坑指南

坑 解决
数据类型错误 训练数据用 float32
KNN k 值不当 通常 k=3~7,奇数
SVM 训练慢 减少数据量或换 LINEAR 核
K-Means 结果随机 增加 attempts

11.7 本集小结

算法 场景 核心 API
KNN 简单分类、OCR KNearest_create().train()
SVM 二/多分类 SVM_create().train()
K-Means 颜色聚类/分割 cv2.kmeans()

← 第10集 第12集:综合实战 →

参考:8_1 KNN | 8_3 SVM | 8_5 K-Means

相关推荐
行走的陀螺仪4 小时前
【转型攻略】2026 年,后端 / 全栈工程师转 AI Agent 方向:能力地图、按周学习路线与面试要点
人工智能·面试·职场和发展
ZhengEnCi4 小时前
2026年10月AI大事盘点:世界模型、开源万亿参数与智能体激战
人工智能·llm·ai编程
hgy89694 小时前
深度学习(小白笔记)——迁徙
人工智能·笔记·深度学习
曲径海盗4 小时前
【Python 学习第 41 天】for 循环
python·学习笔记·编程入门
摇滚侠4 小时前
《On Java 中文版 基础卷》阅读笔记 实现隐藏 11
java·笔记·python
沧海一笑-dj4 小时前
【Python】Python学习笔记-模块与包
python·pip·模块·包·python包·python模块·包与模块
小蒋观天下4 小时前
雷击与浪涌防护:前端SPD选型、接地网误区,接地不当反而引入干扰
深度学习·算法·机器学习
echome8884 小时前
Python 异常处理:try、else、finally 的正确搭配
开发语言·前端·python·异常处理·编程技巧
云和数据.ChenGuang4 小时前
Python 单继承与多继承深度剖析:从 MRO 到实战取舍
开发语言·人工智能·python·langchain