train_cascade 源码阅读之LBP特征

2024-01-12 12:08

本文主要是介绍train_cascade 源码阅读之LBP特征,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文以LBP特征为例,介绍了OpenCV中train_cascade数据初始化的过程。

1 在CvCascadeBoost中,创建了CvCascadeBoostTrainData对象。

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. bool CvCascadeBoost::  
  2. train(  
  3.         const CvFeatureEvaluator* _featureEvaluator,  
  4.         int _numSamples,  
  5.         int _precalcValBufSize, int _precalcIdxBufSize,  
  6.         const CvCascadeBoostParams& _params )  
  7. {  
  8.     bool isTrained = false;  
  9.     CV_Assert( !data );  
  10.     clear();  
  11.     data = new <strong>CvCascadeBoostTrainData</strong>(  
  12.                 _featureEvaluator, _numSamples,  
  13.                 _precalcValBufSize, _precalcIdxBufSize, _params )  
  14.    ……  
  15. }  

2 在CvCascadeBoostTrainData中调用setData函数。

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. CvCascadeBoostTrainData::CvCascadeBoostTrainData( const CvFeatureEvaluator* _featureEvaluator,  
  2.                                                   int _numSamples,  
  3.                                                   int _precalcValBufSize, int _precalcIdxBufSize,  
  4.                                                   const CvDTreeParams& _params )  
  5. {  
  6.    <strong> setData</strong>( _featureEvaluator, _numSamples, _precalcValBufSize, _precalcIdxBufSize, _params );  
  7. }  

3 在setData函数中调用了预先计算特征,也就是参数中preCalcValBufSize, preClacIdxBufSize预留的内存初始化的地方。

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. void CvCascadeBoostTrainData::setData( const CvFeatureEvaluator* _featureEvaluator,  
  2.                                        int _numSamples,  
  3.                                        int _precalcValBufSize, int _precalcIdxBufSize,  
  4.                                        const CvDTreeParams& _params )  
  5. {  
  6.      
  7.  ……  
  8.   
  9.     // precalculate valCache and set indices in buf  
  10.    <strong> precalculate();</strong>  
  11.  ……  
  12.       
  13. }  


4 在precalculate函数中,有初始化时计算一次特征的函数。

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. void CvCascadeBoostTrainData::precalculate()  
  2. {  
  3.     int minNum = MIN( numPrecalcVal, numPrecalcIdx);  
  4.   
  5.     double proctime = -TIME( 0 );  
  6.     parallel_for_( Range(numPrecalcVal, numPrecalcIdx),  
  7.                    FeatureIdxOnlyPrecalc(featureEvaluator, buf, sample_count, is_buf_16u!=0) );  
  8.     parallel_for_( Range(0, minNum),  
  9.                    FeatureValAndIdxPrecalc(featureEvaluator, buf, &valCache, sample_count, is_buf_16u!=0) );  
  10.     parallel_for_( Range(minNum, numPrecalcVal),  
  11.                    <strong>FeatureValOnlyPrecalc(featureEvaluator, &valCache, sample_count) );</strong>  
  12.     cout << "Precalculation time: " << (proctime + TIME( 0 )) << endl;  
  13. }  


5 在该函数内,用函数指针调用了featureEvaluator的一个operator(),如果是LBP特征的话,则调用的是LBP子类的对应函数。

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. struct FeatureValOnlyPrecalc : ParallelLoopBody  
  2. {  
  3.     FeatureValOnlyPrecalc( const CvFeatureEvaluator* _featureEvaluator, Mat* _valCache, int _sample_count )  
  4.     {  
  5.         featureEvaluator = _featureEvaluator;  
  6.         valCache = _valCache;  
  7.         sample_count = _sample_count;  
  8.     }  
  9.     void operator()( const Range& range ) const  
  10.     {  
  11.         for ( int fi = range.start; fi < range.end; fi++)  
  12.             forint si = 0; si < sample_count; si++ )  
  13.                 <strong>valCache->at<float>(fi,si) = (*featureEvaluator)( fi, si );</strong>  
  14.     }  
  15.     const CvFeatureEvaluator* featureEvaluator;  
  16.     Mat* valCache;  
  17.     int sample_count;  
  18. };  


6 在CvLBPEvaluator中定义了operator()虚函数。并且它调用了calc函数,返回计算得到的LBP特征。

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. class CvLBPEvaluator : public CvFeatureEvaluator  
  2. {  
  3. public:  
  4.     virtual ~CvLBPEvaluator() {}  
  5.     virtual void init(  
  6.             const CvFeatureParams   *_featureParams,  
  7.             int                     _maxSampleCount,  
  8.             cv::Size                _winSize );  
  9.     virtual void setImage(  
  10.             const cv::Mat   & img,  
  11.             uchar           clsLabel,  
  12.             int             idx);  
  13.   <strong>  virtual float operator()(  
  14.             int featureIdx,  
  15.             int sampleIdx) const  
  16.     { return (float)features[featureIdx].calc( sum, sampleIdx); }</strong>  
  17.     virtual void writeFeatures(  
  18.             cv::FileStorage &fs,  
  19.             const cv::Mat   &featureMap ) const;  
  20. protected:  
  21.     virtual void generateFeatures();  
  22.   
  23.     class Feature  
  24.     {  
  25.     public:  
  26.         Feature();  
  27.         Feature(  
  28.                 int offset,  
  29.                 int x,  
  30.                 int y,  
  31.                 int _block_w,  
  32.                 int _block_h  );  
  33.         <strong>uchar calc(  
  34.                 const cv::Mat& _sum,  
  35.                 size_t y ) const;</strong>  
  36.         void write( cv::FileStorage &fs ) const;  
  37.   
  38.         cv::Rect rect;  
  39.         <strong>int p[16];</strong>  
  40.     };  
  41.     <strong>std::vector<Feature> features;  
  42.   
  43.     cv::Mat sum;</strong>  
  44. };  

7 在CvLBPEvaluator初始化的时候,初始化了sum矩阵,它有样本和个数相同多的行。

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. void CvLBPEvaluator::init(  
  2.         const CvFeatureParams *_featureParams,  
  3.         int _maxSampleCount, Size _winSize)  
  4. {  
  5.     CV_Assert( _maxSampleCount > 0);  
  6.     <strong>sum.create((int)_maxSampleCount,  
  7.                (_winSize.width + 1) * (_winSize.height + 1),  
  8.                CV_32SC1);</strong>  
  9.     CvFeatureEvaluator::init( _featureParams, _maxSampleCount, _winSize );  
  10. }  


8 创建积分图矩阵,将矩阵的数据指向刚才创建好的sum的对应样本行。

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. void CvLBPEvaluator::setImage(const Mat &img, uchar clsLabel, int idx)  
  2. {  
  3.     CV_DbgAssert( !sum.empty() );  
  4.     CvFeatureEvaluator::setImage( img, clsLabel, idx );  
  5.     Mat <strong>innSum</strong>(winSize.height + 1,  
  6.                winSize.width + 1,  
  7.                sum.type(),  
  8.                <strong>sum.ptr<int>((int)idx)</strong>);  
  9.     <strong>integral</strong>( img, innSum );  
  10. }  


9 调用积分图,返回LBP特征,从函数中可以看到,作者使用的是原始的LBP特征,并没有归一化或者合并等等操作。


        0    |      1    |    2    |    3    

   -------------------------------------

        4    |      5    |    6    |    7    

   -------------------------------------

        8    |      9    |   10 (cval)  |    11   

   -------------------------------------

       12   |     13   |  14   |    15   

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. inline uchar CvLBPEvaluator::Feature::calc(const cv::Mat &_sum, size_t y) const  
  2. {  
  3.     const int* psum = _sum.ptr<int>((int)y);  
  4.     int cval = psum[p[5]] - psum[p[6]] - psum[p[9]] + psum[p[10]];  
  5.   
  6.     return (uchar)(  
  7.             (psum[p[0]] - psum[p[1]] - psum[p[4]] + psum[p[5]] >= cval ? 128 : 0) |   // 0  
  8.             (psum[p[1]] - psum[p[2]] - psum[p[5]] + psum[p[6]] >= cval ? 64 : 0) |    // 1  
  9.             (psum[p[2]] - psum[p[3]] - psum[p[6]] + psum[p[7]] >= cval ? 32 : 0) |    // 2  
  10.             (psum[p[6]] - psum[p[7]] - psum[p[10]] + psum[p[11]] >= cval ? 16 : 0) |  // 5  
  11.             (psum[p[10]] - psum[p[11]] - psum[p[14]] + psum[p[15]] >= cval ? 8 : 0) | // 8  
  12.             (psum[p[9]] - psum[p[10]] - psum[p[13]] + psum[p[14]] >= cval ? 4 : 0) |  // 7  
  13.             (psum[p[8]] - psum[p[9]] - psum[p[12]] + psum[p[13]] >= cval ? 2 : 0) |   // 6  
  14.             (psum[p[4]] - psum[p[5]] - psum[p[8]] + psum[p[9]] >= cval ? 1 : 0));     // 3  
  15. }  


10 需要说明的是,上段代码中的p[?]保存的是图像像素在sum矩阵中的偏移量。这些地址是固定的,在CvFeatureEvaluator初始化的时候创建。

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. void CvFeatureEvaluator::init(const CvFeatureParams *_featureParams,  
  2.                               int _maxSampleCount, Size _winSize )  
  3. {  
  4.     CV_Assert(_maxSampleCount > 0);  
  5.     featureParams = (CvFeatureParams *)_featureParams;  
  6.     winSize = _winSize;  
  7.     numFeatures = 0;  
  8.     cls.create( (int)_maxSampleCount, 1, CV_32FC1 );  
  9.     generateFeatures();  
  10. }  

[cpp]  view plain copy
在CODE上查看代码片 派生到我的代码片
  1. void CvLBPEvaluator::generateFeatures()  
  2. {  
  3.     int offset = winSize.width + 1;  
  4.     forint x = 0; x < winSize.width; x++ )  
  5.         forint y = 0; y < winSize.height; y++ )  
  6.             forint w = 1; w <= winSize.width / 3; w++ )  
  7.                 forint h = 1; h <= winSize.height / 3; h++ )  
  8.                     if ( (x+3*w <= winSize.width) && (y+3*h <= winSize.height) )  
  9.                         features.push_back( Feature(offset, x, y, w, h ) );  
  10.     numFeatures = (int)features.size();  
  11. }  
从这里看,其实现还是和原始的LBP有些不同的。

这篇关于train_cascade 源码阅读之LBP特征的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/597814

相关文章

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

论文阅读笔记: Segment Anything

文章目录 Segment Anything摘要引言任务模型数据引擎数据集负责任的人工智能 Segment Anything Model图像编码器提示编码器mask解码器解决歧义损失和训练 Segment Anything 论文地址: https://arxiv.org/abs/2304.02643 代码地址:https://github.com/facebookresear

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。

音视频入门基础:WAV专题(10)——FFmpeg源码中计算WAV音频文件每个packet的pts、dts的实现

一、引言 从文章《音视频入门基础:WAV专题(6)——通过FFprobe显示WAV音频文件每个数据包的信息》中我们可以知道,通过FFprobe命令可以打印WAV音频文件每个packet(也称为数据包或多媒体包)的信息,这些信息包含该packet的pts、dts: 打印出来的“pts”实际是AVPacket结构体中的成员变量pts,是以AVStream->time_base为单位的显

kubelet组件的启动流程源码分析

概述 摘要: 本文将总结kubelet的作用以及原理,在有一定基础认识的前提下,通过阅读kubelet源码,对kubelet组件的启动流程进行分析。 正文 kubelet的作用 这里对kubelet的作用做一个简单总结。 节点管理 节点的注册 节点状态更新 容器管理(pod生命周期管理) 监听apiserver的容器事件 容器的创建、删除(CRI) 容器的网络的创建与删除

OmniGlue论文详解(特征匹配)

OmniGlue论文详解(特征匹配) 摘要1. 引言2. 相关工作2.1. 广义局部特征匹配2.2. 稀疏可学习匹配2.3. 半稠密可学习匹配2.4. 与其他图像表示匹配 3. OmniGlue3.1. 模型概述3.2. OmniGlue 细节3.2.1. 特征提取3.2.2. 利用DINOv2构建图形。3.2.3. 信息传播与新的指导3.2.4. 匹配层和损失函数3.2.5. 与Super

软件架构模式:5 分钟阅读

原文: https://orkhanscience.medium.com/software-architecture-patterns-5-mins-read-e9e3c8eb47d2 软件架构模式:5 分钟阅读 当有人潜入软件工程世界时,有一天他需要学习软件架构模式的基础知识。当我刚接触编码时,我不知道从哪里获得简要介绍现有架构模式的资源,这样它就不会太详细和混乱,而是非常抽象和易