opencv的dnn解析_dnn.hpp-程序员宅基地

技术标签: caffe  openCV  

在学习过caffemodel加载之后,回头看看这个dnn里面都编译了哪些函数?

先看blob头文件:

#ifndef __OPENCV_DNN_DNN_BLOB_HPP__
#define __OPENCV_DNN_DNN_BLOB_HPP__
#include <opencv2/core.hpp>
#include <vector>
#include <ostream>

namespace cv
{
namespace dnn
{
//该类用来存储和处理blob
    struct BlobShape
    {
        explicit BlobShape(int ndims = 4, int fill = 1);    //!< Creates n-dim shape and fill its by @p fill
        BlobShape(int num, int cn, int rows, int cols);     //创建4-dim shape [num,cn,rows,cols]
        BlobShape(int ndims, const int *sizes);             //创建n-dim的数组
        BlobShape(const std::vector<int> &sizes);           //创建n-dim的vector
        template<int n>
        BlobShape(const Vec<int, n> &shape);                //!< Creates n-dim shape from @ref cv::Vec

	//返回维度的数目
        int dims() const;
	//返回axis的尺度地址,最后的一个axis,为-1,如果不存在,则报错
        int &size(int axis);

        //返回axis的大小
        int size(int axis) const;
	//和size(axis)操作一样
        int operator[](int axis) const; 
	//和size(int) const操作一样
        int &operator[](int axis);

	//和size(int) const操作一样,但是如果不存在axis就返回1.
        int xsize(int axis) const;

	//返回axes的所有大小
        ptrdiff_t total();
	//返回连续数组的第一个元素的指针
        const int *ptr() const;

	//判断两个blob是否相等
        bool equal(const BlobShape &other) const;

        bool operator== (const BlobShape &r) const;

    private:
        cv::AutoBuffer<int,4> sz;
    };

    //提供连续n维cpu和gpu数组的计算方法,该方法支持CPU和GPU的切换和同步
    class CV_EXPORTS Blob
    {
    public:
        explicit Blob();
	//重构blob的尺寸和类型
        explicit Blob(const BlobShape &shape, int type = CV_32F);

        /** @brief Constucts 4-dimensional blob (so-called batch) from image or array of images.
         * @param image 2-dimensional multi-channel or 3-dimensional single-channel image (or array of images)
         * @param dstCn specify size of second axis of ouptut blob
        */
        explicit Blob(InputArray image, int dstCn = -1);
	//创建一个特定维度和类型的blob
        void create(const BlobShape &shape, int type = CV_32F);
	//创建从cv::Mat或cv::UMat获取的数据blob
        void fill(InputArray in);
 
	//如果dedpCopy为false这CPU数据不被分配,然后创建blob
        void fill(const BlobShape &shape, int type, void *data, bool deepCopy = true);

        Mat& matRef();                      //返回cv::mat的地址,包含blob数据
        const Mat& matRefConst() const;     //返回cv::mat的地址,包含blob只读数据
        UMat &umatRef();                    //返回cv::umat的地址,包含blob数据(没有补充数据)
        const UMat &umatRefConst() const;   //返回cv::umat的地址,包含只读blob数据(没有补充数据)
        //返回blob的维度
        int dims() const;
        int size(int axis) const;
        int xsize(int axis) const;
	//计算维度区间,左边是第一维的值包括该值,右边则不包括最后一维的值
        size_t total(int startAxis = 0, int endAxis = INT_MAX) const;

        
	//如果axis范围在0到dims范围之内,则将axis索引转化为标准格式
        int canonicalAxis(int axis) const;

        //返回blob的shape
        BlobShape shape() const;

	//判断两个blob是否相同
        bool equalShape(const Blob &other) const;
	//获取blob的前两维层
        Mat getPlane(int n, int cn);

	//4维blob的shape获取
        int cols() const;       //返回第四维的列数
        int rows() const;       //返回第三维的行数
        int channels() const;   //返回第二维通道数大小
        int num() const;        //返回第一维blob的大小
        Size size2() const;     //返回行和列的尺寸
        Vec4i shape4() const;   //返回前四个blob的axes

        //返回blob中的元素线性索引的坐标
	//如果n<dims()然后未规定的坐标用0,填充
	//如果n>dims()然后多余的坐标被忽视
        template<int n>
        size_t offset(const Vec<int, n> &pos) const;
        //重载offset
        size_t offset(int n = 0, int cn = 0, int row = 0, int col = 0) const;

	//CPU指针的获取
	//返回存储在CPU位置的blob元素指针
	//n与第一个axis相连,cn是第二个axis
	//如果dims()>4然后未规定的坐标用0,填充
	//如果dims()<4然后多余的坐标被忽视
        uchar *ptr(int n = 0, int cn = 0, int row = 0, int col = 0);
        //重载
        template<typename TFloat>
        TFloat *ptr(int n = 0, int cn = 0, int row = 0, int col = 0);
        
	//重载浮点指针
        float *ptrf(int n = 0, int cn = 0, int row = 0, int col = 0);
        //TODO: add const ptr methods
	//和其他blob共享数据,并返回this指针
        Blob &shareFrom(const Blob &blob);

        //重新构建blob的shape
        Blob &reshape(const BlobShape &shape);

        //返回blob的类型
        int type() const;

    private:
        const int *sizes() const;

        Mat m;
    };

//! @}
}
}

#include "blob.inl.hpp"

#endif
其实存储少不了的就是字典,对于查询和使用相当方便,所以看看dict.hpp:
#ifndef __OPENCV_DNN_DNN_DICT_HPP__
#define __OPENCV_DNN_DNN_DICT_HPP__

#include <opencv2/core.hpp>
#include <map>
#include <ostream>

namespace cv
{
namespace dnn
{
//这个结构体将存储标量值或者数组,有一下类型:double类型,cv::String类型,int64类型(使用很少,因为double可以至少存储2^52整数)
struct DictValue
{
    DictValue(const DictValue &r);
    DictValue(int p = 0)        : type(Param::INT), pi(new AutoBuffer<int64,1>) { (*pi)[0] = p; }       //!< Constructs integer scalar
    DictValue(unsigned p)       : type(Param::INT), pi(new AutoBuffer<int64,1>) { (*pi)[0] = p; }       //!< Constructs integer scalar
    DictValue(double p)         : type(Param::REAL), pd(new AutoBuffer<double,1>) { (*pd)[0] = p; }     //!< Constructs floating point scalar
    DictValue(const String &p)  : type(Param::STRING), ps(new AutoBuffer<String,1>) { (*ps)[0] = p; }   //!< Constructs string scalar
    
    template<typename TypeIter>
    static DictValue arrayInt(TypeIter begin, int size);    //!< Constructs integer array
    template<typename TypeIter>
    static DictValue arrayReal(TypeIter begin, int size);   //!< Constructs floating point array
    template<typename TypeIter>
    static DictValue arrayString(TypeIter begin, int size); //!< Constructs array of strings
    
    template<typename T>
    T get(int idx = -1) const; //将带索引的数组元素转换为要求的类型.

    int size() const;
    //判断类型函数
    bool isInt() const;
    bool isString() const;
    bool isReal() const;

    DictValue &operator=(const DictValue &r);

    friend std::ostream &operator<<(std::ostream &stream, const DictValue &dictv);

    ~DictValue();

private:

    int type;

    union
    {
        AutoBuffer<int64, 1> *pi;
        AutoBuffer<double, 1> *pd;
        AutoBuffer<String, 1> *ps;
        void *p;
    };

    DictValue(int _type, void *_p) : type(_type), p(_p) {}
    void release();
};

/** @brief This class implements name-value dictionary, values are instances of DictValue. */
class CV_EXPORTS Dict
{
    typedef std::map<String, DictValue> _Dict;
    _Dict dict;

public:

    
    //检查字典中key值是否存在
    bool has(const String &key);
    //如果key在字典中,然后返回指针的值,否则返回空
    DictValue *ptr(const String &key);
    //如果key在字典中,然后返回指针的值,否则返回错误
    const DictValue &get(const String &key) const;

    //重载
    template <typename T>
    T get(const String &key) const;

    //如果key在字典中,然后返回指针的值,否则返回错误值
    template <typename T>
    T get(const String &key, const T &defaultValue) const;

    //设置新的键值为key或者添加新的key-value对到字典
    template<typename T>
    const T &set(const String &key, const T &value);

    friend std::ostream &operator<<(std::ostream &stream, const Dict &dict);
};

//! @}
}
}

#endif
然后看看dnn.hpp头文件有什么特别之处:
#ifndef __OPENCV_DNN_DNN_HPP__
#define __OPENCV_DNN_DNN_HPP__

#include <vector>
#include <opencv2/core.hpp>
#include <opencv2/dnn/dict.hpp>
#include <opencv2/dnn/blob.hpp>

namespace cv
{
namespace dnn //! This namespace is used for dnn module functionlaity.
{
    //dnn的初始化和layers的建立
    CV_EXPORTS void initModule();

    //这个类为初始化网络提供所有的数据,它包括标量参数字典(参数通过字典地址来读取),blob参数和可选择的元信息(name和layer对象)
    struct CV_EXPORTS LayerParams : public Dict
    {
        std::vector<Blob> blobs; //学习的参数list存储为blob.

        String name; //layer层的名字
        String type; //通过layer工厂创建的layer层名字类型
    };
    //这个接口类允许创建新的layer。从layer派生的每个类必须实现allocate()内存方法来声明它的输出和前向传播计算的输出。在使用新的类之前必须要在layer工厂中注册新的
    //类
    struct CV_EXPORTS Layer
    {
        //学习的参数必须存储在Net::getParam()能够读取到的地方
        std::vector<Blob> blobs;
	//内存的缓冲和blobs的输出必须考虑到输入的shape,参数input是输入blob的vector,out是输出blobs的vector(必须分配内存)
	//这个方法根据输入blob和internal层的参数shape要求产生blob,如果这个函数第一次使用,然后输出包含空的blob vector的尺寸有输出链接决定的。如果输入的blob尺
	//寸被改变了,然后这个方法称为多尺度(翻译不是很恰当)
        virtual void allocate(const std::vector<Blob*> &input, std::vector<Blob> &output) = 0;
	//前向传播,in是输入,out是输出结果
        virtual void forward(std::vector<Blob*> &input, std::vector<Blob> &output) = 0;
	//返回输入数组的索引
	//输入参数是blob的label名字
	//每一层的输入输出通过%<layer_name%>[.output_name]被标记
	//这种方法将输入的label与输入的vector映射起来
        virtual int inputNameToIndex(String inputName);
	//返回输出数组的index
        virtual int outputNameToIndex(String outputName);

        String name; //!< Name of the layer instance, can be used for logging or other internal purposes.
        String type; //!< Type name which was used for creating layer by layer factory.

        Layer();
        explicit Layer(const LayerParams ¶ms); //!< Initialize only #name, #type and #blobs fields.
        virtual ~Layer();
    };
    //这个类允许创建和操作复杂的神经网络。神经网络是一种有向图,顶点是layer的实例化,边是输入输出层之间的关系
    //每个网络层有唯一的ID和唯一的name。
    //LayerId可以存储每个layer的name和layer的id
    class CV_EXPORTS Net
    {
    public:

        Net();  //!< Default constructor.
        ~Net(); //!< Destructor frees the net only if there aren't references to the net anymore.
	//添加一个新的layer到net类中。
	//参数name是添加layer的name。
	//参数类型是经过类型注册后的layer的类型名字。
	//params是初始化layer的。
	//返回创建的layer的ID或者失败返回-1.
        int addLayer(const String &name, const String &type, LayerParams ¶ms);
	//添加层和链接前一层的输出与后一层的输入的添加层
        int addLayerToPrev(const String &name, const String &type, LayerParams ¶ms);
	//转换layer的名字为整数ID,返回值为layer的id,如果layer错误返回-1.
        int getLayerId(const String &layer);
	//封装字符串和整数
        typedef DictValue LayerId;
        //删除layer层
        void deleteLayer(LayerId layer);

        /** 
         * Descriptors have the following template <DFN><layer_name>[.input_number]</DFN>:
         * - the first part of the template <DFN>layer_name</DFN> is sting name of the added layer.
         *   If this part is empty then the network input pseudo layer will be used;
         * - the second optional part of the template <DFN>input_number</DFN>
         *   is either number of the layer input, either label one.
         *   If this part is omitted then the first layer input will be used.
         *
         *  @see setNetInputs(), Layer::inputNameToIndex(), Layer::outputNameToIndex()
         */
	//链接上一层和下一层,上一层的输出作为下一层的输入。
        void connect(String outPin, String inpPin);
        void connect(int outLayerId, int outNum, int inpLayerId, int inpNum);
        // As any other layer, this layer can label its outputs and this function provides an easy way to do this.
	//设置网络输入虚拟层的输出名字,每个网络允许有自己的网络输入虚拟层,id为0.
	//这一层存储使用者的数据,不进行任何计算。
	//事实上,这一层layer提供唯一的方法将自己的数据输入到网络。
	//
        void setNetInputs(const std::vector<String> &inputBlobNames);
        void forward();
        void forward(LayerId toLayer);
	//从网络开始层进行计算,前向传播到输出层
        void forward(LayerId startLayer, LayerId toLayer);
     
        void forward(const std::vector<LayerId> &startLayers, const std::vector<LayerId> &toLayers);
	//优化前向传播,不实现网络,在前面的前向传播之后,进行这些层的前向传播是不变的。
        void forwardOpt(LayerId toLayer);
        /** @overload */
        void forwardOpt(const std::vector<LayerId> &toLayers);
	//设置新的值给输出的blob。
	//outputName是更新层输出blob的描述
	//blob是新的blob
	//如果更新blob不是空,这blob必须有相同shape。
        void setBlob(String outputName, const Blob &blob);
	//返回输出层
	//outputName输出层blob的名字
        Blob getBlob(String outputName);
	//设置新的值给学习参数层。
	//numParam在blob的数组中layer参数的索引。
	//新的blob的值
	//如果新的blob的shape与前面的blob的shape不同,前向传播可能失败
        void setParam(LayerId layer, int numParam, const Blob &blob);
	//返回blob的layer参数
        Blob getParam(LayerId layer, int numParam = 0);

    private:

        struct Impl;
        Ptr<Impl> impl;
    };

    /** @brief Small interface class for loading trained serialized models of different dnn-frameworks. */
    //导入不同的训练好的模型
    class Importer
    {
    public:

	//添加加载的layer到net和layers之间的连接
        virtual void populateNet(Net net) = 0;

        virtual ~Importer();
    };

    //创建网络框架,参数prototxt为配置文件路劲;caffemodel为模型路径。返回导入接口指针,失败则返回空。
    CV_EXPORTS Ptr<Importer> createCaffeImporter(const String &prototxt, const String &caffeModel = String());

    /** @brief Creates the importer of <a href="http://torch.ch">Torch7</a> framework network.
     *  @param filename path to the file, dumped from Torch by using torch.save() function.
     *  @param isBinary specifies whether the network was serialized in ascii mode or binary.
     *  @returns Pointer to the created importer, NULL in failure cases.
     *
     *  @warning Torch7 importer is experimental now, you need explicitly set CMake opencv_dnn_BUILD_TORCH_IMPORTER flag to compile its.
     *
     *  @note Ascii mode of Torch serializer is more preferable, because binary mode extensively use long type of C language,
     *  which has different bit-length on different systems.
     *
     * The loading file must contain serialized <a href="https://github.com/torch/nn/blob/master/doc/module.md">nn.Module</a> object
     * with importing network. Try to eliminate a custom objects from serialazing data to avoid importing errors.
     *
     * List of supported layers (i.e. object instances derived from Torch nn.Module class):
     * - nn.Sequential
     * - nn.Parallel
     * - nn.Concat
     * - nn.Linear
     * - nn.SpatialConvolution
     * - nn.SpatialMaxPooling, nn.SpatialAveragePooling
     * - nn.ReLU, nn.TanH, nn.Sigmoid
     * - nn.Reshape
     *
     * Also some equivalents of these classes from cunn, cudnn, and fbcunn may be successfully imported.
     */
    //这个是torch导入接口,因为torch不熟,所以就不多介绍。
    CV_EXPORTS Ptr<Importer> createTorchImporter(const String &filename, bool isBinary = true);

    /** @brief Loads blob which was serialized as torch.Tensor object of Torch7 framework.
     *  @warning This function has the same limitations as createTorchImporter().
     */
    CV_EXPORTS Blob readTorchBlob(const String &filename, bool isBinary = true);

//! @}
}
}

#include <opencv2/dnn/layer.hpp>
#include <opencv2/dnn/dnn.inl.hpp>

#endif  /* __OPENCV_DNN_DNN_HPP__ */

 最后一部分是layer.hpp:

#ifndef __OPENCV_DNN_LAYER_HPP__
#define __OPENCV_DNN_LAYER_HPP__
#include <opencv2/dnn.hpp>

namespace cv
{
namespace dnn
{
//注册新的layer的工厂模式
class CV_EXPORTS LayerFactory
{
public:
    //每一个layer类必须提供这个函数给factory
    typedef Ptr<Layer>(*Constuctor)(LayerParams ¶ms);
    //注册layer类的类型名字和构造器
    static void registerLayer(const String &type, Constuctor constructor);

    //没有注册的layer类的type name
    static void unregisterLayer(const String &type);
    //创建注册类的实例化,参数有layer名字,初始化layer的参数
    static Ptr<Layer> createLayerInstance(const String &type, LayerParams& params);

private:
    LayerFactory();

    struct Impl;
    static Ptr<Impl> impl();
};

//注册类构造器的运行时间,输入参数有layer的名字,创建注册layer的constructor函数的指针,此宏必须放置在函数代码中。
#define REG_RUNTIME_LAYER_FUNC(type, constuctorFunc) \
    LayerFactory::registerLayer(#type, constuctorFunc);
//注册类的运行时间,输入参数有layer的名字,class是c++ 类,来自layer,此宏必须放置在函数代码中。
#define REG_RUNTIME_LAYER_CLASS(type, class) \
    LayerFactory::registerLayer(#type, _layerDynamicRegisterer<class>);
//在模块加载时注册层构造函数,输入参数有layer的名字,创建注册layer的constructor函数的指针,此宏必须放置在函数代码中。
#define REG_STATIC_LAYER_FUNC(type, constuctorFunc) \
static _LayerStaticRegisterer __LayerStaticRegisterer_##type(#type, constuctorFunc);
//在模块加载时注册层构造函数,输入参数有layer的名字,class是c++ 类,来自layer,此宏必须放置在函数代码中。
#define REG_STATIC_LAYER_CLASS(type, class)                         \
Ptr<Layer> __LayerStaticRegisterer_func_##type(LayerParams ¶ms) \
    { return Ptr<Layer>(new class(params)); }                       \
static _LayerStaticRegisterer __LayerStaticRegisterer_##type(#type, __LayerStaticRegisterer_func_##type);
//下面的类模板是动态注册
template<typename LayerClass>
Ptr<Layer> _layerDynamicRegisterer(LayerParams ¶ms)
{
    return Ptr<Layer>(new LayerClass(params));
}
//允许在模块加载是自动注册创建layer
struct _LayerStaticRegisterer
{
    String type;

    _LayerStaticRegisterer(const String &type, LayerFactory::Constuctor constuctor)
    {
        this->type = type;
        LayerFactory::registerLayer(type, constuctor);
    }

    ~_LayerStaticRegisterer()
    {
        LayerFactory::unregisterLayer(type);
    }
};

}
}
#endif


版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/langb2014/article/details/51286828

智能推荐

什么是内部类?成员内部类、静态内部类、局部内部类和匿名内部类的区别及作用?_成员内部类和局部内部类的区别-程序员宅基地

文章浏览阅读3.4k次,点赞8次,收藏42次。一、什么是内部类?or 内部类的概念内部类是定义在另一个类中的类;下面类TestB是类TestA的内部类。即内部类对象引用了实例化该内部对象的外围类对象。public class TestA{ class TestB {}}二、 为什么需要内部类?or 内部类有什么作用?1、 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。2、内部类可以对同一个包中的其他类隐藏起来。3、 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。三、 内部类的分类成员内部_成员内部类和局部内部类的区别

分布式系统_分布式系统运维工具-程序员宅基地

文章浏览阅读118次。分布式系统要求拆分分布式思想的实质搭配要求分布式系统要求按照某些特定的规则将项目进行拆分。如果将一个项目的所有模板功能都写到一起,当某个模块出现问题时将直接导致整个服务器出现问题。拆分按照业务拆分为不同的服务器,有效的降低系统架构的耦合性在业务拆分的基础上可按照代码层级进行拆分(view、controller、service、pojo)分布式思想的实质分布式思想的实质是为了系统的..._分布式系统运维工具

用Exce分析l数据极简入门_exce l趋势分析数据量-程序员宅基地

文章浏览阅读174次。1.数据源准备2.数据处理step1:数据表处理应用函数:①VLOOKUP函数; ② CONCATENATE函数终表:step2:数据透视表统计分析(1) 透视表汇总不同渠道用户数, 金额(2)透视表汇总不同日期购买用户数,金额(3)透视表汇总不同用户购买订单数,金额step3:讲第二步结果可视化, 比如, 柱形图(1)不同渠道用户数, 金额(2)不同日期..._exce l趋势分析数据量

宁盾堡垒机双因素认证方案_horizon宁盾双因素配置-程序员宅基地

文章浏览阅读3.3k次。堡垒机可以为企业实现服务器、网络设备、数据库、安全设备等的集中管控和安全可靠运行,帮助IT运维人员提高工作效率。通俗来说,就是用来控制哪些人可以登录哪些资产(事先防范和事中控制),以及录像记录登录资产后做了什么事情(事后溯源)。由于堡垒机内部保存着企业所有的设备资产和权限关系,是企业内部信息安全的重要一环。但目前出现的以下问题产生了很大安全隐患:密码设置过于简单,容易被暴力破解;为方便记忆,设置统一的密码,一旦单点被破,极易引发全面危机。在单一的静态密码验证机制下,登录密码是堡垒机安全的唯一_horizon宁盾双因素配置

谷歌浏览器安装(Win、Linux、离线安装)_chrome linux debian离线安装依赖-程序员宅基地

文章浏览阅读7.7k次,点赞4次,收藏16次。Chrome作为一款挺不错的浏览器,其有着诸多的优良特性,并且支持跨平台。其支持(Windows、Linux、Mac OS X、BSD、Android),在绝大多数情况下,其的安装都很简单,但有时会由于网络原因,无法安装,所以在这里总结下Chrome的安装。Windows下的安装:在线安装:离线安装:Linux下的安装:在线安装:离线安装:..._chrome linux debian离线安装依赖

烤仔TVの尚书房 | 逃离北上广?不如押宝越南“北上广”-程序员宅基地

文章浏览阅读153次。中国发达城市榜单每天都在刷新,但无非是北上广轮流坐庄。北京拥有最顶尖的文化资源,上海是“摩登”的国际化大都市,广州是活力四射的千年商都。GDP和发展潜力是衡量城市的数字指...

随便推点

java spark的使用和配置_使用java调用spark注册进去的程序-程序员宅基地

文章浏览阅读3.3k次。前言spark在java使用比较少,多是scala的用法,我这里介绍一下我在项目中使用的代码配置详细算法的使用请点击我主页列表查看版本jar版本说明spark3.0.1scala2.12这个版本注意和spark版本对应,只是为了引jar包springboot版本2.3.2.RELEASEmaven<!-- spark --> <dependency> <gro_使用java调用spark注册进去的程序

汽车零部件开发工具巨头V公司全套bootloader中UDS协议栈源代码,自己完成底层外设驱动开发后,集成即可使用_uds协议栈 源代码-程序员宅基地

文章浏览阅读4.8k次。汽车零部件开发工具巨头V公司全套bootloader中UDS协议栈源代码,自己完成底层外设驱动开发后,集成即可使用,代码精简高效,大厂出品有量产保证。:139800617636213023darcy169_uds协议栈 源代码

AUTOSAR基础篇之OS(下)_autosar 定义了 5 种多核支持类型-程序员宅基地

文章浏览阅读4.6k次,点赞20次,收藏148次。AUTOSAR基础篇之OS(下)前言首先,请问大家几个小小的问题,你清楚:你知道多核OS在什么场景下使用吗?多核系统OS又是如何协同启动或者关闭的呢?AUTOSAR OS存在哪些功能安全等方面的要求呢?多核OS之间的启动关闭与单核相比又存在哪些异同呢?。。。。。。今天,我们来一起探索并回答这些问题。为了便于大家理解,以下是本文的主题大纲:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JCXrdI0k-1636287756923)(https://gite_autosar 定义了 5 种多核支持类型

VS报错无法打开自己写的头文件_vs2013打不开自己定义的头文件-程序员宅基地

文章浏览阅读2.2k次,点赞6次,收藏14次。原因:自己写的头文件没有被加入到方案的包含目录中去,无法被检索到,也就无法打开。将自己写的头文件都放入header files。然后在VS界面上,右键方案名,点击属性。将自己头文件夹的目录添加进去。_vs2013打不开自己定义的头文件

【Redis】Redis基础命令集详解_redis命令-程序员宅基地

文章浏览阅读3.3w次,点赞80次,收藏342次。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。当数据量很大时,count 的数量的指定可能会不起作用,Redis 会自动调整每次的遍历数目。_redis命令

URP渲染管线简介-程序员宅基地

文章浏览阅读449次,点赞3次,收藏3次。URP的设计目标是在保持高性能的同时,提供更多的渲染功能和自定义选项。与普通项目相比,会多出Presets文件夹,里面包含着一些设置,包括本色,声音,法线,贴图等设置。全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,主光源和附加光源在一次Pass中可以一起着色。URP:全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,一次Pass可以计算多个光源。可编程渲染管线:渲染策略是可以供程序员定制的,可以定制的有:光照计算和光源,深度测试,摄像机光照烘焙,后期处理策略等等。_urp渲染管线