点击联系客服
客服QQ: 客服微信:
搜索
热搜: 活动 交友 discuz
Hi~登录注册
查看: 325|回复: 50

OpenCV的“SVM分类器”详细说明:基本原则、函数语法分析和示例代码

[复制链接]

1

主题

1

帖子

-7

积分

限制会员

积分
-7
发表于 2021-10-1 07:27:53 | 显示全部楼层 |阅读模式
文章目录

1.介绍2。基本原则3。函数分析模型生成模型类型设置参数C设置内核函数设置迭代算法的结束标准培训SVM模型预测结果错误计算保存SVM模型保存文件中的SVM加载4。示例代码公式示例(python)推断阶段(C版本)5。摘要

1. 引言

Opencv集成了基于libsvm1的svm接口,允许直接执行视觉分类任务。

Python接口opencv的SVM对数据处理和可视化要求更直观、更方便。

培训模型后,将SVM模型保存为XML,实时应用程序可以通过C接口调用参数文件进行实时估计。

在不平衡样本的分类训练中,使用opencv的SVM基本train函数,分类器容易偏向更多的类别,因此可以使用trainAuto函数来平衡。

如果您对SVM的原理有一定的了解,可以直接进入第3、4节。

2. 基本原理





SVM的目标是找到分割超平面,使分割后的分类结果最坚固,并找到从未见过的样品泛华的最佳2。

在样本空间中划分超平面可以用这个方程来说明。

w

t

x射线

B

=

0

\ bold  symbol  { w  } t  \ bold  symbol  { x  } b=0
">wTx+b=0,其中
   
     
      
      
        w
      
      
        =
      
      
        (
      
      
        
         w
        
        
         1
        
      
      
        ;
      
      
        
         w
        
        
         2
        
      
      
        ;
      
      
        .
      
      
        .
      
      
        .
      
      
        ;
      
      
        
         w
        
        
         d
        
      
      
        )
      
      
      
       \boldsymbol{w}=(w_1;w_2;...;w_d)
      
     
    w=(w1​;w2​;...;wd​)为法向量,决定超平面的方向,b为位移项,决定超平面与原点之间的距离。

对于线性可分的样本空间,需要找到具有最大间隔(maximum margin)的划分超平面,即找到能使下式最大化的参数
   
     
      
      
        w
      
      
      
       \boldsymbol{w}
      
     
    w和b2:

     
      
      
        
         
         
           min
         
         
           ⁡
         
         
         
         
           w
         
         
           ,
         
         
           b
         
         
        
        
         
         
           1
         
         
           2
         
         
         
          ∣
         
         
          ∣
         
         
          w
         
         
          ∣
         
         
         
           ∣
         
         
           2
         
         
        
      
      
         \min_{w,b}{\frac{1}{2}||\boldsymbol{w}||^2}
      
      
     w,bmin​21​∣∣w∣∣2s.t.
     
      
      
        
         
          y
         
         
          i
         
        
        
         (
        
        
         
          w
         
         
          T
         
        
        
         
          x
         
         
          i
         
        
        
         +
        
        
         b
        
        
         )
        
        
         ≥
        
        
         1
        
        
         ,
        
        
         i
        
        
         =
        
        
         1
        
        
         ,
        
        
         2
        
        
         ,
        
        
         .
        
        
         .
        
        
         .
        
        
         ,
        
        
         m
        
      
      
        y_i(\boldsymbol{w}^T\boldsymbol{x_i}+b)≥1,i=1,2,...,m
      
      
     yi​(wTxi​+b)≥1,i=1,2,...,m

对于线性不可分的样本空间,可以将样本从原始空间映射到另一个高维特征空间,从而使样本在这个特征空间内线性可分。由于特征空间的维数可能很高,难以计算,所以通过引入核函数,可以将高维特征空间中的内积(dot product)转化为低维特征空间中的通过核函数计算的结果。

常用核函数2:



为了减少过拟合,引入软间隔(soft margin)概念,允许支持向量机在一些样本上出错:

     
      
      
        
         
          y
         
         
          i
         
        
        
         (
        
        
         
          w
         
         
          T
         
        
        
         
          x
         
         
          i
         
        
        
         +
        
        
         b
        
        
         )
        
        
         ≥
        
        
         1
        
      
      
        y_i(\boldsymbol{w}^T\boldsymbol{x_i}+b)≥1
      
      
     yi​(wTxi​+b)≥1

用参数C来约束分类出错的样本,松弛变量
   
     
      
      
        
         ξ
        
        
         i
        
      
      
      
       ξ_i
      
     
    ξi​表示训练样本距离对应的正确决策边界的距离,对于分类正确的样本距离即为03。



优化问题调整为:

     
      
      
        
         m
        
        
         i
        
        
         
          n
         
         
         
           w
         
         
           ,
         
         
           
            b
           
           
            0
           
         
         
        
        
         
          ∣
         
         
          ∣
         
         
          w
         
         
          ∣
         
         
         
           ∣
         
         
           2
         
         
         
          +
         
         
          C
         
         
         
           ∑
         
         
           i
         
         
         
         
           ξ
         
         
           i
         
         
        
      
      
        min_{\boldsymbol{w},b_0}{||\boldsymbol{w}||^2+C\sum_i{ξ_i}}
      
      
     minw,b0​​∣∣w∣∣2+Ci∑​ξi​

s.t.
     
      
      
        
         
          y
         
         
          i
         
        
        
         (
        
        
         
          w
         
         
          T
         
        
        
         
          x
         
         
          i
         
        
        
         +
        
        
         
          b
         
         
          0
         
        
        
         )
        
        
         ≥
        
        
         1
        
        
         −
        
        
         
          ξ
         
         
          i
         
        
        
         ,
        
        
         且
        
        
         
          ξ
         
         
          i
         
        
        
         ≥
        
        
         0
        
        
         ∀
        
        
         i
        
      
      
         y_i(\boldsymbol{w}^T\boldsymbol{x_i}+b_0)≥1-ξ_i,且ξ_i≥0 ∀i
      
      
     yi​(wTxi​+b0​)≥1−ξi​,且ξi​≥0∀i

3. 函数解析
SVM类在opencv中的继承关系如图所示4:


SVM继承自StatModel和Algorithm类。

在opencv中使用SVM的一般流程如下:


  #mermaid-svg-yT2e6la6j76c2qro .label{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);fill:#333;color:#333}#mermaid-svg-yT2e6la6j76c2qro .label text{fill:#333}#mermaid-svg-yT2e6la6j76c2qro .node rect,#mermaid-svg-yT2e6la6j76c2qro .node circle,#mermaid-svg-yT2e6la6j76c2qro .node ellipse,#mermaid-svg-yT2e6la6j76c2qro .node polygon,#mermaid-svg-yT2e6la6j76c2qro .node path{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-yT2e6la6j76c2qro .node .label{text-align:center;fill:#333}#mermaid-svg-yT2e6la6j76c2qro .node.clickable{cursor:pointer}#mermaid-svg-yT2e6la6j76c2qro .arrowheadPath{fill:#333}#mermaid-svg-yT2e6la6j76c2qro .edgePath .path{stroke:#333;stroke-width:1.5px}#mermaid-svg-yT2e6la6j76c2qro .flowchart-link{stroke:#333;fill:none}#mermaid-svg-yT2e6la6j76c2qro .edgeLabel{background-color:#e8e8e8;text-align:center}#mermaid-svg-yT2e6la6j76c2qro .edgeLabel rect{opacity:0.9}#mermaid-svg-yT2e6la6j76c2qro .edgeLabel span{color:#333}#mermaid-svg-yT2e6la6j76c2qro .cluster rect{fill:#ffffde;stroke:#aa3;stroke-width:1px}#mermaid-svg-yT2e6la6j76c2qro .cluster text{fill:#333}#mermaid-svg-yT2e6la6j76c2qro div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:12px;background:#ffffde;border:1px solid #aa3;border-radius:2px;pointer-events:none;z-index:100}#mermaid-svg-yT2e6la6j76c2qro .actor{stroke:#ccf;fill:#ECECFF}#mermaid-svg-yT2e6la6j76c2qro text.actor>tspan{fill:#000;stroke:none}#mermaid-svg-yT2e6la6j76c2qro .actor-line{stroke:grey}#mermaid-svg-yT2e6la6j76c2qro .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333}#mermaid-svg-yT2e6la6j76c2qro .messageLine1{stroke-width:1.5;stroke-dasharray:2, 2;stroke:#333}#mermaid-svg-yT2e6la6j76c2qro #arrowhead path{fill:#333;stroke:#333}#mermaid-svg-yT2e6la6j76c2qro .sequenceNumber{fill:#fff}#mermaid-svg-yT2e6la6j76c2qro #sequencenumber{fill:#333}#mermaid-svg-yT2e6la6j76c2qro #crosshead path{fill:#333;stroke:#333}#mermaid-svg-yT2e6la6j76c2qro .messageText{fill:#333;stroke:#333}#mermaid-svg-yT2e6la6j76c2qro .labelBox{stroke:#ccf;fill:#ECECFF}#mermaid-svg-yT2e6la6j76c2qro .labelText,#mermaid-svg-yT2e6la6j76c2qro .labelText>tspan{fill:#000;stroke:none}#mermaid-svg-yT2e6la6j76c2qro .loopText,#mermaid-svg-yT2e6la6j76c2qro .loopText>tspan{fill:#000;stroke:none}#mermaid-svg-yT2e6la6j76c2qro .loopLine{stroke-width:2px;stroke-dasharray:2, 2;stroke:#ccf;fill:#ccf}#mermaid-svg-yT2e6la6j76c2qro .note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-yT2e6la6j76c2qro .noteText,#mermaid-svg-yT2e6la6j76c2qro .noteText>tspan{fill:#000;stroke:none}#mermaid-svg-yT2e6la6j76c2qro .activation0{fill:#f4f4f4;stroke:#666}#mermaid-svg-yT2e6la6j76c2qro .activation1{fill:#f4f4f4;stroke:#666}#mermaid-svg-yT2e6la6j76c2qro .activation2{fill:#f4f4f4;stroke:#666}#mermaid-svg-yT2e6la6j76c2qro .mermaid-main-font{font-family:"trebuchet ms", verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro .section{stroke:none;opacity:0.2}#mermaid-svg-yT2e6la6j76c2qro .section0{fill:rgba(102,102,255,0.49)}#mermaid-svg-yT2e6la6j76c2qro .section2{fill:#fff400}#mermaid-svg-yT2e6la6j76c2qro .section1,#mermaid-svg-yT2e6la6j76c2qro .section3{fill:#fff;opacity:0.2}#mermaid-svg-yT2e6la6j76c2qro .sectionTitle0{fill:#333}#mermaid-svg-yT2e6la6j76c2qro .sectionTitle1{fill:#333}#mermaid-svg-yT2e6la6j76c2qro .sectionTitle2{fill:#333}#mermaid-svg-yT2e6la6j76c2qro .sectionTitle3{fill:#333}#mermaid-svg-yT2e6la6j76c2qro .sectionTitle{text-anchor:start;font-size:11px;text-height:14px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro .grid .tick{stroke:#d3d3d3;opacity:0.8;shape-rendering:crispEdges}#mermaid-svg-yT2e6la6j76c2qro .grid .tick text{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro .grid path{stroke-width:0}#mermaid-svg-yT2e6la6j76c2qro .today{fill:none;stroke:red;stroke-width:2px}#mermaid-svg-yT2e6la6j76c2qro .task{stroke-width:2}#mermaid-svg-yT2e6la6j76c2qro .taskText{text-anchor:middle;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro .taskText:not([font-size]){font-size:11px}#mermaid-svg-yT2e6la6j76c2qro .taskTextOutsideRight{fill:#000;text-anchor:start;font-size:11px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro .taskTextOutsideLeft{fill:#000;text-anchor:end;font-size:11px}#mermaid-svg-yT2e6la6j76c2qro .task.clickable{cursor:pointer}#mermaid-svg-yT2e6la6j76c2qro .taskText.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-yT2e6la6j76c2qro .taskTextOutsideLeft.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-yT2e6la6j76c2qro .taskTextOutsideRight.clickable{cursor:pointer;fill:#003163 !important;font-weight:bold}#mermaid-svg-yT2e6la6j76c2qro .taskText0,#mermaid-svg-yT2e6la6j76c2qro .taskText1,#mermaid-svg-yT2e6la6j76c2qro .taskText2,#mermaid-svg-yT2e6la6j76c2qro .taskText3{fill:#fff}#mermaid-svg-yT2e6la6j76c2qro .task0,#mermaid-svg-yT2e6la6j76c2qro .task1,#mermaid-svg-yT2e6la6j76c2qro .task2,#mermaid-svg-yT2e6la6j76c2qro .task3{fill:#8a90dd;stroke:#534fbc}#mermaid-svg-yT2e6la6j76c2qro .taskTextOutside0,#mermaid-svg-yT2e6la6j76c2qro .taskTextOutside2{fill:#000}#mermaid-svg-yT2e6la6j76c2qro .taskTextOutside1,#mermaid-svg-yT2e6la6j76c2qro .taskTextOutside3{fill:#000}#mermaid-svg-yT2e6la6j76c2qro .active0,#mermaid-svg-yT2e6la6j76c2qro .active1,#mermaid-svg-yT2e6la6j76c2qro .active2,#mermaid-svg-yT2e6la6j76c2qro .active3{fill:#bfc7ff;stroke:#534fbc}#mermaid-svg-yT2e6la6j76c2qro .activeText0,#mermaid-svg-yT2e6la6j76c2qro .activeText1,#mermaid-svg-yT2e6la6j76c2qro .activeText2,#mermaid-svg-yT2e6la6j76c2qro .activeText3{fill:#000 !important}#mermaid-svg-yT2e6la6j76c2qro .done0,#mermaid-svg-yT2e6la6j76c2qro .done1,#mermaid-svg-yT2e6la6j76c2qro .done2,#mermaid-svg-yT2e6la6j76c2qro .done3{stroke:grey;fill:#d3d3d3;stroke-width:2}#mermaid-svg-yT2e6la6j76c2qro .doneText0,#mermaid-svg-yT2e6la6j76c2qro .doneText1,#mermaid-svg-yT2e6la6j76c2qro .doneText2,#mermaid-svg-yT2e6la6j76c2qro .doneText3{fill:#000 !important}#mermaid-svg-yT2e6la6j76c2qro .crit0,#mermaid-svg-yT2e6la6j76c2qro .crit1,#mermaid-svg-yT2e6la6j76c2qro .crit2,#mermaid-svg-yT2e6la6j76c2qro .crit3{stroke:#f88;fill:red;stroke-width:2}#mermaid-svg-yT2e6la6j76c2qro .activeCrit0,#mermaid-svg-yT2e6la6j76c2qro .activeCrit1,#mermaid-svg-yT2e6la6j76c2qro .activeCrit2,#mermaid-svg-yT2e6la6j76c2qro .activeCrit3{stroke:#f88;fill:#bfc7ff;stroke-width:2}#mermaid-svg-yT2e6la6j76c2qro .doneCrit0,#mermaid-svg-yT2e6la6j76c2qro .doneCrit1,#mermaid-svg-yT2e6la6j76c2qro .doneCrit2,#mermaid-svg-yT2e6la6j76c2qro .doneCrit3{stroke:#f88;fill:#d3d3d3;stroke-width:2;cursor:pointer;shape-rendering:crispEdges}#mermaid-svg-yT2e6la6j76c2qro .milestone{transform:rotate(45deg) scale(0.8, 0.8)}#mermaid-svg-yT2e6la6j76c2qro .milestoneText{font-style:italic}#mermaid-svg-yT2e6la6j76c2qro .doneCritText0,#mermaid-svg-yT2e6la6j76c2qro .doneCritText1,#mermaid-svg-yT2e6la6j76c2qro .doneCritText2,#mermaid-svg-yT2e6la6j76c2qro .doneCritText3{fill:#000 !important}#mermaid-svg-yT2e6la6j76c2qro .activeCritText0,#mermaid-svg-yT2e6la6j76c2qro .activeCritText1,#mermaid-svg-yT2e6la6j76c2qro .activeCritText2,#mermaid-svg-yT2e6la6j76c2qro .activeCritText3{fill:#000 !important}#mermaid-svg-yT2e6la6j76c2qro .titleText{text-anchor:middle;font-size:18px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro g.classGroup text{fill:#9370db;stroke:none;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family);font-size:10px}#mermaid-svg-yT2e6la6j76c2qro g.classGroup text .title{font-weight:bolder}#mermaid-svg-yT2e6la6j76c2qro g.clickable{cursor:pointer}#mermaid-svg-yT2e6la6j76c2qro g.classGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-yT2e6la6j76c2qro g.classGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro .classLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5}#mermaid-svg-yT2e6la6j76c2qro .classLabel .label{fill:#9370db;font-size:10px}#mermaid-svg-yT2e6la6j76c2qro .relation{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-yT2e6la6j76c2qro .dashed-line{stroke-dasharray:3}#mermaid-svg-yT2e6la6j76c2qro #compositionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro #compositionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro #aggregationStart{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro #aggregationEnd{fill:#ECECFF;stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro #dependencyStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro #dependencyEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro #extensionStart{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro #extensionEnd{fill:#9370db;stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro .commit-id,#mermaid-svg-yT2e6la6j76c2qro .commit-msg,#mermaid-svg-yT2e6la6j76c2qro .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro .pieTitleText{text-anchor:middle;font-size:25px;fill:#000;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro .slice{font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro g.stateGroup text{fill:#9370db;stroke:none;font-size:10px;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro g.stateGroup text{fill:#9370db;fill:#333;stroke:none;font-size:10px}#mermaid-svg-yT2e6la6j76c2qro g.statediagram-cluster .cluster-label text{fill:#333}#mermaid-svg-yT2e6la6j76c2qro g.stateGroup .state-title{font-weight:bolder;fill:#000}#mermaid-svg-yT2e6la6j76c2qro g.stateGroup rect{fill:#ECECFF;stroke:#9370db}#mermaid-svg-yT2e6la6j76c2qro g.stateGroup line{stroke:#9370db;stroke-width:1}#mermaid-svg-yT2e6la6j76c2qro .transition{stroke:#9370db;stroke-width:1;fill:none}#mermaid-svg-yT2e6la6j76c2qro .stateGroup .composit{fill:white;border-bottom:1px}#mermaid-svg-yT2e6la6j76c2qro .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px}#mermaid-svg-yT2e6la6j76c2qro .state-note{stroke:#aa3;fill:#fff5ad}#mermaid-svg-yT2e6la6j76c2qro .state-note text{fill:black;stroke:none;font-size:10px}#mermaid-svg-yT2e6la6j76c2qro .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.7}#mermaid-svg-yT2e6la6j76c2qro .edgeLabel text{fill:#333}#mermaid-svg-yT2e6la6j76c2qro .stateLabel text{fill:#000;font-size:10px;font-weight:bold;font-family:'trebuchet ms', verdana, arial;font-family:var(--mermaid-font-family)}#mermaid-svg-yT2e6la6j76c2qro .node circle.state-start{fill:black;stroke:black}#mermaid-svg-yT2e6la6j76c2qro .node circle.state-end{fill:black;stroke:white;stroke-width:1.5}#mermaid-svg-yT2e6la6j76c2qro #statediagram-barbEnd{fill:#9370db}#mermaid-svg-yT2e6la6j76c2qro .statediagram-cluster rect{fill:#ECECFF;stroke:#9370db;stroke-width:1px}#mermaid-svg-yT2e6la6j76c2qro .statediagram-cluster rect.outer{rx:5px;ry:5px}#mermaid-svg-yT2e6la6j76c2qro .statediagram-state .divider{stroke:#9370db}#mermaid-svg-yT2e6la6j76c2qro .statediagram-state .title-state{rx:5px;ry:5px}#mermaid-svg-yT2e6la6j76c2qro .statediagram-cluster.statediagram-cluster .inner{fill:white}#mermaid-svg-yT2e6la6j76c2qro .statediagram-cluster.statediagram-cluster-alt .inner{fill:#e0e0e0}#mermaid-svg-yT2e6la6j76c2qro .statediagram-cluster .inner{rx:0;ry:0}#mermaid-svg-yT2e6la6j76c2qro .statediagram-state rect.basic{rx:5px;ry:5px}#mermaid-svg-yT2e6la6j76c2qro .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#efefef}#mermaid-svg-yT2e6la6j76c2qro .note-edge{stroke-dasharray:5}#mermaid-svg-yT2e6la6j76c2qro .statediagram-note rect{fill:#fff5ad;stroke:#aa3;stroke-width:1px;rx:0;ry:0}:root{--mermaid-font-family: '"trebuchet ms", verdana, arial';--mermaid-font-family: "Comic Sans MS", "Comic Sans", cursive}#mermaid-svg-yT2e6la6j76c2qro .error-icon{fill:#522}#mermaid-svg-yT2e6la6j76c2qro .error-text{fill:#522;stroke:#522}#mermaid-svg-yT2e6la6j76c2qro .edge-thickness-normal{stroke-width:2px}#mermaid-svg-yT2e6la6j76c2qro .edge-thickness-thick{stroke-width:3.5px}#mermaid-svg-yT2e6la6j76c2qro .edge-pattern-solid{stroke-dasharray:0}#mermaid-svg-yT2e6la6j76c2qro .edge-pattern-dashed{stroke-dasharray:3}#mermaid-svg-yT2e6la6j76c2qro .edge-pattern-dotted{stroke-dasharray:2}#mermaid-svg-yT2e6la6j76c2qro .marker{fill:#333}#mermaid-svg-yT2e6la6j76c2qro .marker.cross{stroke:#333}
:root { --mermaid-font-family: "trebuchet ms", verdana, arial;}
  #mermaid-svg-yT2e6la6j76c2qro {
    color: rgba(0, 0, 0, 0.75);
    font: ;
  }
  
   
   
   
     
      
      
      
        
      
      
     
     
      
      
      
        
      
      
     
     
      
      
      
        
      
      
     
     
      
      
      
        
      
      
     
     
      
      
      
        
      
      
     
     
      
      
      
        
      
      
     
     
      
      
      
        
      
      
     
   
   
     
      
      
      
        
         训练
        
      
      
     
     
      
      
      
        
         推理
        
      
      
     
     
      
      
      
        
         
        
      
      
     
     
      
      
      
        
         
        
      
      
     
     
      
      
      
        
         
        
      
      
     
     
      
      
      
        
         
        
      
      
     
     
      
      
      
        
         
        
      
      
     
   
   
     
      
      
      
        
         
          开始
         
        
      
      
     
     
      
      
      
        
         
          创建SVM模型
         
        
      
      
     
     
      
      
      
        
         
          加载SVM模型
         
        
      
      
     
     
      
      
      
        
         
          配置参数
         
        
      
      
     
     
      
      
      
        
         
          加载训练数据
         
        
      
      
     
     
      
      
      
        
         
          模型训练
         
        
      
      
     
     
      
      
      
        
         
          保存模型
         
        
      
      
     
     
      
      
      
        
         
          输入数据进行预测
         
        
      
      
     
   
   
  


创建模型
C++:

static PtrSVM> cv::ml::SVM::create()

Python:

cv.ml.SVM_create() -> retval

设置模型类型
C++:

enum  Types {
  C_SVC =100,//C-支持向量分类。n级分类(n≥ 2) 允许使用异常值的惩罚乘数 C 不完全地分离类。
  NU_SVC =101,//ν-支持向量分类。n级分类,可能有不完美的分离。参数ν用于代替C,参数ν在0-1范围内,值越大,决策边界越平滑。
  ONE_CLASS =102,//分布估计,所有的训练数据都来自同一个类,SVM 构建了一个边界,将类与特征空间的其余部分分开。
  EPS_SVR =103,//ε-支持向量回归。来自训练集的特征向量和拟合超平面之间的距离必须小于p。对于异常值,使用惩罚乘数 C。
  NU_SVR =104 // ν-支持向量回归。 ν用于代替 p。
}
virtual void cv::ml::SVM::setType(int val)

Python:

cv.ml_SVM.setType(val) ->None

设置参数C
根据"2.基本原理"中对参数C的介绍,我们应该如何设置参数C?

  • 较大的 C值给出了误分类错误较少但余量较小的解决方案。考虑到在这种情况下犯错误分类错误的代价很高。由于优化的目的是最小化参数,因此允许出现很少的误分类错误。
  • 较小的C值给出了具有更大余量和更多分类错误的解决方案。在这种情况下,最小化不会考虑那么多的总和项,因此它更侧重于寻找具有大余量的超平面。
    C++:

    //设置参数C
    virtual void cv::ml::SVM::setC(double val)

    python:

    cv.ml_SVM.setC(val) -> None

    设置核函数
    C++:

    enum KernelTypes {
      CUSTOM =-1,//由SVM::getKernelType返回,默认是RBF
      LINEAR =0,//线性内核,速度最快
      POLY =1,//多项式核
      RBF =2,//径向基函数(RBF),大多数情况下是个不错的选择
      SIGMOID =3,//sigmoid核
      CHI2 =4,//Chi2核,类似于RBF核
      INTER =5//直方图交叉核,速度较快
    }
    virtual void cv::ml::SVM::setKernel(int kernelType)   

    python:

    cv.ml_SVM.setKernel(kernelType) -> None

    设置迭代算法的终止标准
    C++:

    virtual void cv::ml::SVM::setTermCriteria(const cv::TermCriteria &val)   
    // cv::TermCriteria
    cv::TermCriteria::TermCriteria (int type,int maxCount,double epsilon)
    // Type
    enum  cv::TermCriteria::Type {
      COUNT =1,
      MAX_ITER =COUNT,//最大迭代次数
      EPS =2 //迭代算法停止时所需的精度或参数变化
    }

    python:

    cv.ml_SVM.setTermCriteria(val) ->None

    训练SVM模型
    trainAuto方法通过选择最佳参数 C、gamma、p、nu、coef0、degree 来自动训练 SVM 模型。当测试集误差的交叉验证估计最小时,参数被认为是最佳的。此函数仅使用SVM::getDefaultGrid进行参数优化,因此仅提供基本的参数选项。

    trainAuto函数适用于分类(SVM::C_SVC或SVM::NU_SVC)以及回归(SVM::EPS_SVR或SVM::NU_SVR)。如果是SVM::ONE_CLASS,则不进行优化,并执行带有 params 中指定参数的常用 SVM。

    C++:

    //输入由TrainData::create或TrainData::loadFromCSV构造的训练数据
    virtual bool cv::ml::SVM::trainAuto(const PtrTrainData> & data,
    int     kFold = 10,
    ParamGrid     Cgrid = getDefaultGrid(C),
    ParamGrid     gammaGrid = getDefaultGrid(GAMMA),
    ParamGrid     pGrid = getDefaultGrid(P),
    ParamGrid     nuGrid = getDefaultGrid(NU),
    ParamGrid     coeffGrid = getDefaultGrid(COEF),
    ParamGrid     degreeGrid = getDefaultGrid(DEGREE),
    bool     balanced = false
    )   
    //输入训练样本
    bool cv::ml::SVM::trainAuto(InputArray samples,
        int     layout,
        InputArray     responses,
        int     kFold = 10,
        Ptr ParamGrid >     Cgrid = SVM::getDefaultGridPtr(SVM::C),
        Ptr ParamGrid >     gammaGrid = SVM::getDefaultGridPtr(SVM::GAMMA),
        Ptr ParamGrid >     pGrid = SVM::getDefaultGridPtr(SVM::P),
        Ptr ParamGrid >     nuGrid = SVM::getDefaultGridPtr(SVM::NU),
        Ptr ParamGrid >     coeffGrid = SVM::getDefaultGridPtr(SVM::COEF),
        Ptr ParamGrid >     degreeGrid = SVM::getDefaultGridPtr(SVM::DEGREE),
        bool     balanced = false
    )

    Python:

    cv.ml_SVM.trainAuto(samples, layout, responses[, kFold[, Cgrid[, gammaGrid[, pGrid[, nuGrid[, coeffGrid[, degreeGrid[, balanced]]]]]]]]) -> retval

    参数:

  • samples:训练样本
  • layout:参考 ml::SampleTypes,如cv.ml.ROW_SAMPLE表示每个训练样本是行向量,cv.ml.COL_SAMPLE表示每个训练样本是列向量
  • responses:与训练样本有关的响应向量
  • kFold:k交叉验证,训练集会分成k个子集,从中选取一个用来测试,剩余k-1个用来训练
  • balanced:如果设为True且是2-class分类问题,方法会自动创建更平衡的交叉验证子集,即子集中的类之间比例接近整个训练数据集中的比例
    预测结果
    C++:

    // 预测输入样本的响应结果
    virtual float predict(
      InputArray  samples,                  // input samples, float matrix
      OutputArray results = cv::noArray(),  // optional output results matrix
      int         flags   = 0               // (model-dependent)
    ) const = 0;

    python:

    cv.ml_StatModel.predict(samples[, results[, flags]]) ->retval, results

    误差计算
    对于回归模型,误差计算为 RMS;对于分类器,误差计算为错误分类样本的百分比 (0%-100%)。
    C++:

    // 在训练集或测试集上计算误差
    virtual float calcError(
      const PtrTrainData>& data, // training samples
      bool test, // true: compute over test set
          // false: compute over training set
      cv::OutputArray resp  // the optional output responses
    ) const;

    python:

    cv.ml_StatModel.calcError(data, test[, resp]) ->retval, resp

    保存SVM模型
    C++:

    void cv::Algorithm::save(const String &filename)  const

    Python:

    cv.Algorithm.save(filename) ->None

    从文件中加载SVM
    C++:

    static PtrSVM> cv::ml::SVM::load(const String &filepath)

    Python:

    cv.ml.SVM_load(filepath) ->retval

    4. 示例代码
    官方示例(python)

    构造数据,用来模拟训练集中的两个类别:

    from __future__ import print_function
    import cv2 as cv
    import numpy as np
    import random as rng
    import time
    from matplotlib import pyplot as plt
    NTRAINING_SAMPLES = 100 # 每个类别的训练样本数
    FRAC_LINEAR_SEP = 0.9   # 线性可分的样本比例
    # 准备用于数据可视化
    WIDTH = 512
    HEIGHT = 512
    I = np.zeros((HEIGHT, WIDTH, 3), dtype=np.uint8)
    # 设置训练样本
    trainData = np.empty((2*NTRAINING_SAMPLES, 2), dtype=np.float32)
    labels = np.empty((2*NTRAINING_SAMPLES, 1), dtype=np.int32)
    rng.seed(100) # Random value generation class
    # 线性可分的训练样本数量
    nLinearSamples = int(FRAC_LINEAR_SEP * NTRAINING_SAMPLES)
    ## [setup1]
    # 生成class 1的随机点,随机点的x坐标在[0, 0.4),y坐标在 [0, 1)
    trainClass = trainData[0:nLinearSamples,:]
    # The x coordinate of the points is in [0, 0.4)
    c = trainClass[:,0:1]
    c[:] = np.random.uniform(0.0, 0.4 * WIDTH, c.shape)
    # The y coordinate of the points is in [0, 1)
    c = trainClass[:,1:2]
    c[:] = np.random.uniform(0.0, HEIGHT, c.shape)
    # 生成class 2的随机点,随机点的x坐标在[0.6, 1],y坐标在 [0, 1)
    trainClass = trainData[2*NTRAINING_SAMPLES-nLinearSamples:2*NTRAINING_SAMPLES,:]
    # The x coordinate of the points is in [0.6, 1]
    c = trainClass[:,0:1]
    c[:] = np.random.uniform(0.6*WIDTH, WIDTH, c.shape)
    # The y coordinate of the points is in [0, 1)
    c = trainClass[:,1:2]
    c[:] = np.random.uniform(0.0, HEIGHT, c.shape)
    # 设置线性不可分的训练样本
    # Generate random points for the classes 1 and 2
    trainClass = trainData[nLinearSamples:2*NTRAINING_SAMPLES-nLinearSamples,:]
    # x坐标在 [0.4, 0.6),y坐标在[0, 1)
    c = trainClass[:,0:1]
    c[:] = np.random.uniform(0.4*WIDTH, 0.6*WIDTH, c.shape)
    c = trainClass[:,1:2]
    c[:] = np.random.uniform(0.0, HEIGHT, c.shape)
    # 设置两个类别的label
    labels[0:NTRAINING_SAMPLES,:] = 1                   # Class 1
    labels[NTRAINING_SAMPLES:2*NTRAINING_SAMPLES,:] = 2 # Class 2

    设置SVM参数,初始化模型:

    print('Starting training process')
    svm = cv.ml.SVM_create()
    svm.setType(cv.ml.SVM_C_SVC)
    svm.setC(0.1)
    svm.setKernel(cv.ml.SVM_LINEAR)
    svm.setTermCriteria((cv.TERM_CRITERIA_MAX_ITER, int(1e7), 1e-6))

    训练SVM:

    ## 训练
    svm.train(trainData, cv.ml.ROW_SAMPLE, labels)
    print('Finished training process')
    ## 显示决策区域
    green = (0,100,0)
    blue = (100,0,0)
    for i in range(I.shape[0]):
        for j in range(I.shape[1]):
            sampleMat = np.matrix([[j,i]], dtype=np.float32)
            response = svm.predict(sampleMat)[1]
            if response == 1:
                I[i,j] = green
            elif response == 2:
                I[i,j] = blue

    对训练集中两个类别的样本进行可视化:

    ## 用两种颜色圆圈表示class 1和class 2的训练数据
    thick = -1
    # Class 1
    for i in range(NTRAINING_SAMPLES):
        px = trainData[i,0]
        py = trainData[i,1]
        cv.circle(I, (px, py), 3, (0, 255, 0), thick)
    # Class 2
    for i in range(NTRAINING_SAMPLES, 2*NTRAINING_SAMPLES):
        px = trainData[i,0]
        py = trainData[i,1]
        cv.circle(I, (px, py), 3, (255, 0, 0), thick)
    # 显示支持向量(
    ## [show_vectors]
    thick = 2
    sv = svm.getUncompressedSupportVectors()
    for i in range(sv.shape[0]):
        cv.circle(I, (sv[i,0], sv[i,1]), 6, (128, 128, 128), thick)
    ## [show_vectors]
    #cv.imwrite('result.png', I)                      # save the Image
    #cv.imshow('SVM for Non-Linear Training Data', I) # show it to the user
    plt.imshow(I)

    推理阶段(C++版本)
    void test_svm(std::string videopath, std::string svm_file = "svm.mat")
    {
        /// 加载svm模型参数
        cv::Ptrcv::ml::SVM> svm = cv::ml::SVM::load(svm_file);
        /// 初始化特征提取器
        // 此处省略……
       
        cv::VideoCapture cap(videopath);
        if (cap.isOpened())
        {
            cv::Mat src;//img
            int sleep_interval = 1;//每隔多少ms取帧
            int frameIdx = 0;
            while (true)
            {
                if (!cap.read(src))
                {
                    break;
                }
                frameIdx++;
                double start = static_castdouble>(cv::getTickCount());
                cv::Mat flowFeat;
                
                //提取运动特征
                m_featureExtactor.ProcessFlow(src, flowFeat);
                flowFeat.convertTo(flowFeat, CV_32FC1);
                
                //获取分类结果
                int response = (int)svm->predict(flowFeat);
                cv::putText(src, cv::String(std::to_string(response)), cv::Point(20,20), cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 255, 0));  
                
                //计算耗时
                float times = ((float)cv::getTickCount() - start) / cv::getTickFrequency();
                std::cout  "time cost: "  times  " s."  std::endl;
                cv::imshow("img", src);
                if (cv::waitKey(1) == 27) {
                    break;
                }
            }
        }
    }

    5. 小结
    本文整理了Opencv中SVM支持向量机的原理、函数和代码示例。

    如果对你有帮助的话,欢迎一键三连支持下博主~



    Chih-Chung Chang and Chih-Jen Lin. Libsvm: a library for support vector machines. ACM Transactions on Intelligent Systems and Technology (TIST), 2(3):27, 2011. ↩︎
    《机器学习》周志华 ↩︎ ↩︎ ↩︎
    https://docs.opencv.org/4.5.3/d4/db1/tutorial_py_svm_basics.html ↩︎
    https://docs.opencv.org/4.5.3/d1/d2d/classcv_1_1ml_1_1SVM.html ↩︎
    [/ol]
  • 回复

    使用道具 举报

    1

    主题

    401

    帖子

    -257

    积分

    限制会员

    积分
    -257
    发表于 2021-10-1 07:42:54 | 显示全部楼层
    我是个凑数的。。。
    回复

    使用道具 举报

    1

    主题

    394

    帖子

    -214

    积分

    限制会员

    积分
    -214
    发表于 2021-10-1 08:17:49 | 显示全部楼层
    写的真的很不错
    回复

    使用道具 举报

    1

    主题

    358

    帖子

    -223

    积分

    限制会员

    积分
    -223
    发表于 2021-10-1 08:40:41 | 显示全部楼层
    看帖回帖是美德!
    回复

    使用道具 举报

    1

    主题

    373

    帖子

    -247

    积分

    限制会员

    积分
    -247
    发表于 2021-10-1 09:02:30 | 显示全部楼层
    帮帮顶顶!!
    回复

    使用道具 举报

    1

    主题

    366

    帖子

    -192

    积分

    限制会员

    积分
    -192
    发表于 2021-10-1 09:34:19 | 显示全部楼层
    难得一见的好帖
    回复

    使用道具 举报

    0

    主题

    374

    帖子

    -225

    积分

    限制会员

    积分
    -225
    发表于 2021-10-1 10:09:59 | 显示全部楼层
    我是个凑数的。。。
    回复

    使用道具 举报

    0

    主题

    388

    帖子

    -251

    积分

    限制会员

    积分
    -251
    发表于 2021-10-1 10:31:26 | 显示全部楼层
    路过,支持一下啦
    回复

    使用道具 举报

    1

    主题

    396

    帖子

    -234

    积分

    限制会员

    积分
    -234
    发表于 2021-10-1 11:13:54 | 显示全部楼层
    学习了,不错,讲的太有道理了
    回复

    使用道具 举报

    1

    主题

    367

    帖子

    -211

    积分

    限制会员

    积分
    -211
    发表于 2021-10-1 11:35:10 | 显示全部楼层
    没看完~~~~~~ 先顶,好同志
    回复

    使用道具 举报

    游客
    回复
    您需要登录后才可以回帖 登录 | 立即注册

    快速回复 返回顶部 返回列表