【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

相关推荐
Joy T8 分钟前
Spring AI 接入已有 Java 项目的三种架构设计
java·人工智能·springai·ai入门·chatclient·ai service·ai能力接入
m4Rk_16 分钟前
【论文阅读】Agent 记忆机制(94):MemGen——在推理过程中动态生成并织入潜在记忆
论文阅读·人工智能·学习·开源·github
IT_陈寒17 分钟前
Redis卡顿的锅,这次真不是大key的错
前端·人工智能·后端
小鹿的周先生26 分钟前
第11章-Structured-Output
开发语言·人工智能·python
175******6317327 分钟前
灰片调色适合什么素材
人工智能
大模型真好玩31 分钟前
从 SDK 到成熟智能体:拆解 LangChain、Pi、DeepSeek Harness、Claude Code的本质区别
人工智能·agent·deepseek
程序哥聊面试33 分钟前
什么是 Sandbox?给 AI Agent 划一道安全边界
人工智能·安全
梦在远山后42 分钟前
通过 WSS 让 Server 安全调用 Desktop 本地工具(下01):Ticket、人工确认、幂等与断线对账
python·langchain·agent
大虾别跑1 小时前
ai-daily-2026-10-08
人工智能·chatgpt
adinnet20261 小时前
保单、赔付与渠道问数:保险经营数据如何实现按需查询
大数据·数据库·人工智能