layer_factory.hpp/layer_factory.cpp代码解析

本文详细解析了Caffe框架中网络层的注册与创建机制,包括如何通过宏定义实现不同类型的网络层注册,以及运行时如何根据配置参数实例化正确的网络层。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
 * @brief A layer factory that allows one to register layers.
 * During runtime, registered layers could be called by passing a LayerParameter
 * protobuffer to the CreateLayer function:
 *
 *     LayerRegistry<Dtype>::CreateLayer(param);
 *
 * There are two ways to register a layer. Assuming that we have a layer like:
 *
 *   template <typename Dtype>
 *   class MyAwesomeLayer : public Layer<Dtype> {
 *     // your implementations
 *   };
 *
 * and its type is its C++ class name, but without the "Layer" at the end
 * ("MyAwesomeLayer" -> "MyAwesome").
 *
 * If the layer is going to be created simply by its constructor, in your c++
 * file, add the following line:
 *
 *    REGISTER_LAYER_CLASS(MyAwesome);
 *
 * Or, if the layer is going to be created by another creator function, in the
 * format of:
 *
 *    template <typename Dtype>
 *    Layer<Dtype*> GetMyAwesomeLayer(const LayerParameter& param) {
 *      // your implementation
 *    }
 *
 * (for example, when your layer has multiple backends, see GetConvolutionLayer
 * for a use case), then you can register the creator function instead, like
 *
 * REGISTER_LAYER_CREATOR(MyAwesome, GetMyAwesomeLayer)
 *
 * Note that each layer type should only be registered once.
 */

#ifndef CAFFE_LAYER_FACTORY_H_
#define CAFFE_LAYER_FACTORY_H_

#include <map>
#include <string>
#include <vector>

#include "caffe/common.hpp"
#include "caffe/layer.hpp"
#include "caffe/proto/caffe.pb.h"

namespace caffe {

template <typename Dtype>
class Layer;

template <typename Dtype>
class LayerRegistry {
 public:
  typedef shared_ptr<Layer<Dtype> > (*Creator)(const LayerParameter&);
  typedef std::map<string, Creator> CreatorRegistry;

  /* 创建map<string, Creator>容器,该容器将包含所有layer层描述字符串和对应的Creator函数,
     Creator函数用于实例化对应的layer层,
	 部分网络层的Creator函数是通过REGISTER_LAYER_CLASS宏实现的,
	 如absval、accuracy网络层,对应的Creator函数为:
	 template <typename Dtype>                                                    \
	 shared_ptr<Layer<Dtype> > Creator_##type##Layer(const LayerParameter& param) \
	 {                                                                            \
	 return shared_ptr<Layer<Dtype> >(new type##Layer<Dtype>(param));           \
	 }

	 部分网络层的Creator函数在layer_factory.cpp文件实现的,
	 如Convolution、Pooling层,对应的Creator函数为:
	 GetConvolutionLayer、GetPoolingLayer。

	 以上两种方法都是通过宏REGISTER_LAYER_CREATOR(type, Creator_##type##Layer)进行注册的。
	 该宏会实例化LayerRegisterer<float>,LayerRegisterer<double>类,
	 然后会调用类的构造函数:
	 LayerRegisterer(const string& type, shared_ptr<Layer<Dtype> > (*creator)(const LayerParameter&))
	 {
		LayerRegistry<Dtype>::AddCreator(type, creator);
	 }
	 该构造函数会调用LayerRegistry<Dtype>::AddCreator(type, creator)函数,
	 将网络层type及对应的creator函数添加到map<string, Creator>容器中
	 */
  static CreatorRegistry& Registry() {
    static CreatorRegistry* g_registry_ = new CreatorRegistry();
    return *g_registry_;
  }

  /* 被LayerRegisterer构造函数调用,增加网络层type对应的creator */
  static void AddCreator(const string& type, Creator creator) {
    CreatorRegistry& registry = Registry();
    CHECK_EQ(registry.count(type), 0)
        << "Layer type " << type << " already registered.";
    registry[type] = creator;
  }

  /* 被Net<Dtype>::Init函数调用,实例化对应的网络层 */
  static shared_ptr<Layer<Dtype> > CreateLayer(const LayerParameter& param) {
    if (Caffe::root_solver()) {
      LOG(INFO) << "Creating layer " << param.name();
    }
    const string& type = param.type();
    CreatorRegistry& registry = Registry();
    CHECK_EQ(registry.count(type), 1) << "Unknown layer type: " << type
        << " (known types: " << LayerTypeListString() << ")";
    return registry[type](param);
  }

  /* 将所有的网络层type存放在vector<string>中,被LayerTypeListString调用 */
  static vector<string> LayerTypeList() {
    CreatorRegistry& registry = Registry();
    vector<string> layer_types;
    for (typename CreatorRegistry::iterator iter = registry.begin();
         iter != registry.end(); ++iter) {
      layer_types.push_back(iter->first);
    }
    return layer_types;
  }

 private:
  // Layer registry should never be instantiated - everything is done with its
  // static variables.
  LayerRegistry() {}

  /* 将所有的网络层type转化为字符串string, CreateLayer调用 */
  static string LayerTypeListString() {
    vector<string> layer_types = LayerTypeList();
    string layer_types_str;
    for (vector<string>::iterator iter = layer_types.begin();
         iter != layer_types.end(); ++iter) {
      if (iter != layer_types.begin()) {
        layer_types_str += ", ";
      }
      layer_types_str += *iter;
    }
    return layer_types_str;
  }
};


template <typename Dtype>
class LayerRegisterer {
 public:
  LayerRegisterer(const string& type,
                  shared_ptr<Layer<Dtype> > (*creator)(const LayerParameter&)) {
    // LOG(INFO) << "Registering layer type: " << type;
    LayerRegistry<Dtype>::AddCreator(type, creator);
  }
};


#define REGISTER_LAYER_CREATOR(type, creator)                                  \
  static LayerRegisterer<float> g_creator_f_##type(#type, creator<float>);     \
  static LayerRegisterer<double> g_creator_d_##type(#type, creator<double>)    \

#define REGISTER_LAYER_CLASS(type)                                             \
  template <typename Dtype>                                                    \
  shared_ptr<Layer<Dtype> > Creator_##type##Layer(const LayerParameter& param) \
  {                                                                            \
    return shared_ptr<Layer<Dtype> >(new type##Layer<Dtype>(param));           \
  }                                                                            \
  REGISTER_LAYER_CREATOR(type, Creator_##type##Layer)

}  // namespace caffe

#endif  // CAFFE_LAYER_FACTORY_H_

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

layer_factory.hpp中Registry()函数重复调用,g_registry_空间是否会从新分配。

static CreatorRegistry& Registry() {
    static CreatorRegistry* g_registry_ = new CreatorRegistry();
    return *g_registry_;
  }

验证代码:

#include "stdio.h"

class test
{
public:

	static int& Registry()
	{
		static int* g_registry_ = new int();
		printf("address:%p\n", g_registry_);
		return *g_registry_;
	}
};

int main()
{
	printf("hello world\n");
	int& i =  test::Registry();
	int& j =  test::Registry();
	
	return 0;
}

地址打印相同,证明g_registry_不会从新分配,静态变量只在第一次new时分配空间。

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

absval_layer.cpp

宏展开方法:
g++ -E -o cc.i src/caffe/layers/absval_layer.cpp -MMD -MP -pthread -fPIC -DCAFFE_VERSION=1.0.0 -DDEBUG -g -O0 -DUSE_CUDNN -DUSE_OPENCV -DUSE_LEVELDB -DUSE_LMDB -DWITH_PYTHON_LAYER -I/usr/include/python2.7 -I/usr/local/lib/python2.7/dist-packages/numpy/core/include -I/usr/local/include -I/usr/include/hdf5/serial -I.build_debug/src -I./src -I./include -I/usr/local/cuda/include -Wall -Wno-sign-compare

1: 
INSTANTIATE_CLASS(AbsValLayer);展开得:
char gInstantiationGuardAbsValLayer; 
template class AbsValLayer<float>; 
template class AbsValLayer<double>;
作用是模板具化,具化类:AbsValLayer<float>和AbsValLayer<double>


REGISTER_LAYER_CLASS(AbsVal);展开得:
/* 实例化AbsValLayer<Dtype>网络层 */
template <typename Dtype> shared_ptr<Layer<Dtype> > Creator_AbsValLayer(const LayerParameter& param) 
{
    return shared_ptr<Layer<Dtype> >(new AbsValLayer<Dtype>(param));
}
/* 网络层注册 */
static LayerRegisterer<float> g_creator_f_AbsVal("AbsVal", Creator_AbsValLayer<float>); 
static LayerRegisterer<double> g_creator_d_AbsVal("AbsVal", Creator_AbsValLayer<double>);

g_creator_f_AbsVal和g_creator_d_AbsVal实例化,会调用构造函数:
template <typename Dtype>
class LayerRegisterer
{
public:
    LayerRegisterer(const string& type, shared_ptr<Layer<Dtype> > (*creator)(const LayerParameter&))
    {
        // LOG(INFO) << "Registering layer type: " << type;
        LayerRegistry<Dtype>::AddCreator(type, creator);
    }
};
这时就将网络层AbsVal和Creator_AbsValLayer<float>加入到map容器中

另外有些网络层之间在layer_factory.cpp中使用宏:
#define REGISTER_LAYER_CREATOR(type, creator)注册
如卷基层、池化层等:
REGISTER_LAYER_CREATOR(Convolution, GetConvolutionLayer);
REGISTER_LAYER_CREATOR(Pooling, GetPoolingLayer);

综上,有两类网络层注册方式,分别为:
REGISTER_LAYER_CLASS(AbsVal)和REGISTER_LAYER_CREATOR(Pooling, GetPoolingLayer);
底层都使用了宏和REGISTER_LAYER_CREATOR
 

07-20
<think>我们遇到了两个相关的问题: 1. `dictionary.hpp`文件中的`#ifndef`未终止(导致后续条件编译混乱) 2. 类`cv::aruco::Dictionary`重复定义(可能由于上述宏未终止,导致同一个头文件被包含多次时,类定义重复出现) 根本原因分析: 在`dictionary.hpp`中,开头应该有: #ifndef __OPENCV_DICTIONARY_HPP__ #define __OPENCV_DICTIONARY_HPP__ 并且结尾有: #endif 但是错误提示表明在文件第39行(即`#ifndef`行)之后没有找到对应的`#endif`,导致条件编译指令未正确关闭。 解决方案: 1. 手动修复`dictionary.hpp`文件,确保其有正确的包含保护(include guard)。 2. 如果问题是由于多个版本的OpenCV头文件混合使用导致的,则需要统一OpenCV版本。 具体步骤: 步骤1:检查并修复头文件 打开文件:`/home/yxz/PX4_Firmware/Tools/simulation/gazebo-classic/sitl_gazebo-classic/include/opencv2/aruco/dictionary.hpp` 检查文件末尾是否有`#endif`。如果没有,则添加。 通常,该文件应该具有如下结构: ``` #ifndef __OPENCV_DICTIONARY_HPP__ #define __OPENCV_DICTIONARY_HPP__ ... // 文件内容 #endif ``` 步骤2:如果文件已经包含`#endif`,则可能是条件编译宏的名称不一致。注意检查: - 开头的宏名称和结尾的宏名称是否匹配? - 是否在中间有嵌套的条件编译导致提前结束? 步骤3:如果文件本身没有问题,则可能是由于其他原因导致预处理器处理错误。可以尝试以下方法: - 清理编译缓存:`make clean`,然后重新编译。 - 更新代码:确保PX4固件是最新的,特别是子模块(包括sitl_gazebo-classic)。 步骤4:检查OpenCV版本 由于PX4的sitl_gazebo-classic使用了自己内置的OpenCV aruco模块(在`Tools/simulation/gazebo-classic/sitl_gazebo-classic/openCV_aruco`目录下),而系统可能安装了其他版本的OpenCV。如果系统OpenCV的头文件被错误地包含,可能会导致冲突。 建议: - 确保编译时优先使用sitl_gazebo-classic内置的OpenCV aruco模块。 - 如果系统安装了OpenCV,尝试卸载或者确保编译时不会包含系统OpenCV的aruco头文件。 步骤5:如果问题仍然存在,可以尝试修改有问题的头文件,更换一个唯一的宏名称(避免与其他头文件冲突)。例如: ``` #ifndef PX4_GAZEBO_DICTIONARY_HPP_ #define PX4_GAZEBO_DICTIONARY_HPP_ ... #endif // PX4_GAZEBO_DICTIONARY_HPP_ ``` 具体操作: 方法A:手动修复文件(如果缺少#endif) - 打开文件:`vim /home/yxz/PX4_Firmware/Tools/simulation/gazebo-classic/sitl_gazebo-classic/include/opencv2/aruco/dictionary.hpp` - 在文件末尾添加:`#endif // __OPENCV_DICTIONARY_HPP__` 方法B:使用sed命令修复(如果确认缺少#endif) ``` echo "#endif // __OPENCV_DICTIONARY_HPP__" >> /home/yxz/PX4_Firmware/Tools/simulation/gazebo-classic/sitl_gazebo-classic/include/opencv2/aruco/dictionary.hpp ``` 方法C:如果文件中已经存在#endif,但编译器仍然报错,则可能是宏名冲突。可以尝试修改宏名称(注意:修改后可能影响其他依赖,需谨慎): ``` sed -i 's/__OPENCV_DICTIONARY_HPP__/PX4_GAZEBO_DICTIONARY_HPP_/g' /home/yxz/PX4_Firmware/Tools/simulation/gazebo-classic/sitl_gazebo-classic/include/opencv2/aruco/dictionary.hpp ``` 同时将文件开头两行改为: ``` #ifndef PX4_GAZEBO_DICTIONARY_HPP_ #define PX4_GAZEBO_DICTIONARY_HPP_ ``` 方法D:更新PX4固件代码(推荐) - 进入PX4固件目录:`cd /home/yxz/PX4_Firmware` - 更新主仓库:`git pull` - 更新子模块:`git submodule update --init --recursive` - 重新编译:`make px4_sitl gazebo-classic` 方法E:如果更新代码后问题仍然存在,可能是子模块(sitl_gazebo-classic)中的该头文件确实存在bug,可以尝试在PX4的GitHub仓库中提交issue,或者查找是否已有相关修复。 注意:由于sitl_gazebo-classic是PX4的一个子模块,它的代码可能不是最新的,或者存在分支差异。确保使用正确的分支(如与PX4固件版本匹配的分支)。 预防措施: - 在编译之前,经常更新代码和子模块。 - 如果修改了子模块的代码,注意备份,因为更新子模块可能会覆盖修改。 相关问题: 1. 如何解决OpenCV头文件中的宏定义冲突? 2. 在PX4编译过程中如何正确更新子模块? 3. 如何避免C++头文件重复包含? 4. 如何检查C++预处理器处理后的代码以调试宏错误? 生成相关问题: 1. 如何修复`dictionary.hpp`头文件中的宏定义错误? 2. 在PX4 Gazebo仿真中,如何更新aruco模块? 3. 如何解决类`cv::aruco::Dictionary`重复定义的问题? 4. 如何为PX4编译指定使用系统OpenCV而不是内置模块?</think>### 解决OpenCV Aruco模块的宏未终止和类重复定义问题 #### 问题根本原因 1. **未终止的宏定义 (`#ifndef`)** ```cpp // 错误示例:缺少对应的#endif #ifndef __OPENCV_DICTIONARY_HPP__ // [第39行] #define __OPENCV_DICTIONARY_HPP__ class Dictionary { /*...*/ }; // [^1] ``` 导致预处理器状态混乱,后续代码被异常包含。 2. **类重复定义 (`cv::aruco::Dictionary`)** ```cpp class Dictionary { /*...*/ }; // 第一次定义 // 由于宏未终止,同一头文件被二次包含 class Dictionary { /*...*/ }; // 重复定义! ``` 未终止的宏使头文件保护失效,引发类重复定义[^2]。 --- ### 解决方案 #### ✅ 方案1:修复头文件宏定义(推荐) ```bash # 进入头文件目录 cd ~/PX4_Firmware/Tools/simulation/gazebo-classic/sitl_gazebo-classic/include/opencv2/aruco # 添加缺失的#endif echo -e "\n#endif // __OPENCV_DICTIONARY_HPP__" >> dictionary.hpp # 验证修复 tail -n 3 dictionary.hpp ``` 输出应显示: ```cpp #endif // __OPENCV_DICTIONARY_HPP__ ``` #### ✅ 方案2:更新PX4源码(同步修复) ```bash cd ~/PX4_Firmware git submodule update --init --recursive # 更新所有子模块 find . -name "dictionary.hpp" -exec sed -i '$a#endif // __OPENCV_DICTIONARY_HPP__' {} + make clean make px4_sitl gazebo-classic # [^3] ``` #### ✅ 方案3:使用系统OpenCV(绕过内置模块) ```cmake # 修改CMakeLists.txt (路径: Tools/simulation/gazebo-classic/sitl_gazebo-classic/CMakeLists.txt) # 注释掉内置模块 # add_subdirectory(openCV_aruco) # 添加系统OpenCV find_package(OpenCV 4.2 REQUIRED COMPONENTS aruco) # 指定版本 include_directories(${OpenCV_INCLUDE_DIRS}) target_link_libraries(gazebo_aruco_plugin ${OpenCV_LIBS}) ``` --- ### 关键验证步骤 1. **检查宏完整性** ```bash # 确认头文件结构 grep -A 5 -B 1 "Dictionary" include/opencv2/aruco/dictionary.hpp ``` 正确结构: ```cpp #ifndef __OPENCV_DICTIONARY_HPP__ #define __OPENCV_DICTIONARY_HPP__ class Dictionary { /* ... */ }; #endif ``` 2. **编译测试** ```bash make px4_sitl gazebo-classic -j$(nproc) | grep "dictionary" ``` 无输出表示成功 3. **版本兼容检查** ```bash # 检查OpenCV版本一致性 grep "OPENCV_VERSION" build/CMakeCache.txt pkg-config --modversion opencv4 ``` 输出应相同(推荐4.2.0+)[^4] --- ### 预防措施 1. **Git钩子自动检查** ```bash # .git/hooks/pre-commit #!/bin/sh grep -L "#endif" $(find . -name "*.hpp") && exit 1 || exit 0 ``` 2. **编译缓存配置** ```bash export CC="clang" export CXX="clang++" export CCACHE_CPP2=yes # 改进预处理缓存 ``` 3. **头文件扫描工具** ```bash # 安装include-what-you-use sudo apt install iwyu make clean make VERBOSE=1 px4_sitl 2>&1 | iwyu_tool.py -p . ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值