Add more docs

pull/265/head
Vitaliy Lyudvichenko 9 years ago
parent 945094bc58
commit c681508872
  1. 11
      modules/dnn/include/opencv2/dnn.hpp
  2. 69
      modules/dnn/include/opencv2/dnn/blob.hpp
  3. 22
      modules/dnn/include/opencv2/dnn/blob.inl.hpp
  4. 322
      modules/dnn/include/opencv2/dnn/dict.hpp
  5. 124
      modules/dnn/include/opencv2/dnn/dnn.hpp
  6. 298
      modules/dnn/include/opencv2/dnn/dnn.inl.hpp
  7. 4
      modules/dnn/src/caffe/caffe_importer.cpp
  8. 4
      modules/dnn/src/dnn.cpp
  9. 4
      modules/dnn/src/layers/concat_layer.cpp
  10. 2
      modules/dnn/src/layers/convolution_layer.cpp
  11. 4
      modules/dnn/src/layers/mvn_layer.cpp
  12. 4
      modules/dnn/src/layers/slice_layer.cpp
  13. 2
      modules/dnn/src/layers/softmax_layer.cpp
  14. 2
      modules/dnn/src/layers/split_layer.cpp
  15. 4
      modules/dnn/src/torch/torch_importer.cpp
  16. 2
      modules/dnn/test/test_common.hpp
  17. 2
      modules/dnn/test/test_layers.cpp
  18. 4
      modules/dnn/test/test_torch_importer.cpp

@ -49,11 +49,16 @@
/** @defgroup dnn Deep Neural Network module
@{
This module contain tools to load artifical neural network models and to make forward test passes.
This module contains:
- API for new layers creation, layers are building bricks of neural networks;
- set of built-in most-useful Layers;
- API to constuct and modify comprehensive neural networks from layers;
- functionality for loading serialized networks models from differnet frameworks.
Functionality of this module is designed only for forward pass computations (i. e. network testing).
A network training is in principle not supported.
@}
*/
#include <opencv2/dnn/dnn.hpp>
#endif /* __OPENCV_DNN_HPP__ */

@ -49,8 +49,10 @@ namespace cv
{
namespace dnn
{
/** @brief Lightweight class for storing and processing a shape of blob (or anything else).
*/
//! @addtogroup dnn
//! @{
/** @brief Lightweight class for storing and processing a shape of blob (or anything else). */
struct BlobShape
{
explicit BlobShape(int ndims = 4, int fill = 1); //!< Creates n-dim shape and fill its by @p fill
@ -72,7 +74,7 @@ namespace dnn
int &size(int axis);
/** @brief Returns the size of the specified @p axis.
* @see size()
* @see size()
*/
int size(int axis) const;
@ -95,19 +97,17 @@ namespace dnn
/** @brief Checks equality of two shapes. */
bool equal(const BlobShape &other) const;
bool operator== (const BlobShape &r) const;
private:
cv::AutoBuffer<int,4> sz;
};
bool operator== (const BlobShape &l, const BlobShape &r);
//maybe useless
CV_EXPORTS std::ostream &operator<< (std::ostream &stream, const BlobShape &shape);
/** @brief Provides convenient methods for continuous n-dimensional array processing, dedicated for convolution neural networks.
/** @brief This class provides methods for continuous n-dimensional CPU and GPU array processing.
*
* It's realized as wrapper over @ref cv::Mat and @ref cv::UMat and will support methods for CPU/GPU switching.
* The class is realized as a wrapper over @ref cv::Mat and @ref cv::UMat.
* It will support methods for switching and logical synchronization between CPU and GPU.
*/
class CV_EXPORTS Blob
{
@ -117,21 +117,26 @@ namespace dnn
/** @brief Constructs blob with specified @p shape and @p type. */
explicit Blob(const BlobShape &shape, int type = CV_32F);
/** @brief Constucts 4-dimensional blob from image or array of images.
/** @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 if specified force size of ouptut blob channel-dimension
* @param dstCn specify size of second axis of ouptut blob
*/
explicit Blob(InputArray image, int dstCn = -1);
/** @brief Creates blob with specified @p shape and @p type. */
void create(const BlobShape &shape, int type = CV_32F);
/** @brief Creates blob from cv::Mat or cv::UMat without copying the data */
void fill(InputArray in);
/** @brief Creates blob from user data.
* @details If @p deepCopy is false then CPU data will not be allocated.
*/
void fill(const BlobShape &shape, int type, void *data, bool deepCopy = true);
Mat& getMatRef();
const Mat& getMatRef() const;
//TODO: add UMat get methods
Mat& matRef(); //!< Returns reference to cv::Mat, containing blob data.
const Mat& matRefConst() const; //!< Returns reference to cv::Mat, containing blob data, for read-only purposes.
UMat &umatRef(); //!< Returns reference to cv::UMat, containing blob data (not implemented yet).
const UMat &umatRefConst() const; //!< Returns reference to cv::UMat, containing blob data, for read-only purposes (not implemented yet).
/** @brief Returns number of blob dimensions. */
int dims() const;
@ -155,12 +160,10 @@ namespace dnn
* @param startAxis the first axis to include in the range.
* @param endAxis the first axis to exclude from the range.
* @details Negative axis indexing can be used.
* @see canonicalAxis()
*/
size_t total(int startAxis = 0, int endAxis = INT_MAX) const;
/** @brief converts @p axis index to canonical format (where 0 <= axis < dims())
*/
/** @brief Converts @p axis index to canonical format (where 0 <= axis < dims()). */
int canonicalAxis(int axis) const;
/** @brief Returns shape of the blob. */
@ -177,24 +180,38 @@ namespace dnn
/** @addtogroup Shape getters of 4-dimensional blobs.
* @{
*/
int cols() const; //!< Returns size of the fourth blob axis.
int rows() const; //!< Returns size of the thrid blob axis.
int channels() const; //!< Returns size of the second blob axis.
int num() const; //!< Returns size of the first blob axis.
int cols() const; //!< Returns size of the fourth axis blob.
int rows() const; //!< Returns size of the thrid axis blob.
int channels() const; //!< Returns size of the second axis blob.
int num() const; //!< Returns size of the first axis blob.
Size size2() const; //!< Returns cv::Size(cols(), rows())
Vec4i shape4() const; //!< Returns shape of firt four blob axes.
/** @}*/
/** @addtogroup CPU pointer getters
* @{
/** @brief Returns linear index of the element with specified coordinates in the blob.
*
* If @p n < dims() then unspecified coordinates will be filled by zeros.
* If @p n > dims() then extra coordinates will be ignored.
*/
template<int n>
size_t offset(const Vec<int, n> &pos) const;
/** @overload */
size_t offset(int n = 0, int cn = 0, int row = 0, int col = 0) const;
/** @addtogroup CPU pointer getters
* @{
*/
/** @brief Returns pointer to the blob element with the specified position, stored in CPU memory.
*
* @p n correspond to the first axis, @p cn - to the second, etc.
* If dims() > 4 then unspecified coordinates will be filled by zeros.
* If dims() < 4 then extra coordinates will be ignored.
*/
uchar *ptr(int n = 0, int cn = 0, int row = 0, int col = 0);
/** @overload */
template<typename TFloat>
TFloat *ptr(int n = 0, int cn = 0, int row = 0, int col = 0);
/** Returns (float*) ptr() */
/** @overload ptr<float>() */
float *ptrf(int n = 0, int cn = 0, int row = 0, int col = 0);
//TODO: add const ptr methods
/** @}*/
@ -217,6 +234,8 @@ namespace dnn
Mat m;
};
//! @}
}
}

@ -150,12 +150,14 @@ inline bool BlobShape::equal(const BlobShape &other) const
return true;
}
inline bool operator== (const BlobShape &l, const BlobShape &r)
inline bool BlobShape::operator==(const BlobShape &r) const
{
return l.equal(r);
return this->equal(r);
}
CV_EXPORTS std::ostream &operator<< (std::ostream &stream, const BlobShape &shape);
/////////////////////////////////////////////////////////////////////
inline int Blob::canonicalAxis(int axis) const
{
@ -258,16 +260,28 @@ inline bool Blob::equalShape(const Blob &other) const
return true;
}
inline Mat& Blob::getMatRef()
inline Mat& Blob::matRef()
{
return m;
}
inline const Mat& Blob::getMatRef() const
inline const Mat& Blob::matRefConst() const
{
return m;
}
inline UMat &Blob::umatRef()
{
CV_Error(Error::StsNotImplemented, "");
return *(new UMat());
}
inline const UMat &Blob::umatRefConst() const
{
CV_Error(Error::StsNotImplemented, "");
return *(new UMat());
}
inline Mat Blob::getPlane(int n, int cn)
{
CV_Assert(dims() > 2);

@ -50,24 +50,29 @@ namespace cv
{
namespace dnn
{
//! @addtogroup dnn
//! @{
/** @brief This struct stores the scalar value (or array) of one of the following type: double, cv::String or int64.
* @todo Maybe int64 is useless because double type exactly stores at least 2^52 integers.
*/
struct DictValue
{
DictValue(const DictValue &r);
DictValue(int p = 0) : type(Param::INT), pi(new AutoBuffer<int64,1>) { (*pi)[0] = p; }
DictValue(unsigned p) : type(Param::INT), pi(new AutoBuffer<int64,1>) { (*pi)[0] = p; }
DictValue(double p) : type(Param::REAL), pd(new AutoBuffer<double,1>) { (*pd)[0] = p; }
DictValue(const String &p) : type(Param::STRING), ps(new AutoBuffer<String,1>) { (*ps)[0] = p; }
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);
static DictValue arrayInt(TypeIter begin, int size); //!< Constructs integer array
template<typename TypeIter>
static DictValue arrayReal(TypeIter begin, int size);
static DictValue arrayReal(TypeIter begin, int size); //!< Constructs floating point array
template<typename TypeIter>
static DictValue arrayString(TypeIter begin, int size);
static DictValue arrayString(TypeIter begin, int size); //!< Constructs array of strings
template<typename T>
T get(int idx = -1) const;
T get(int idx = -1) const; //!< Tries to convert array element with specified index to requested type and returns it.
int size() const;
@ -81,7 +86,7 @@ struct DictValue
~DictValue();
protected:
private:
int type;
@ -97,33 +102,7 @@ protected:
void release();
};
template<typename TypeIter>
DictValue DictValue::arrayInt(TypeIter begin, int size)
{
DictValue res(Param::INT, new AutoBuffer<int64, 1>(size));
for (int j = 0; j < size; begin++, j++)
(*res.pi)[j] = *begin;
return res;
}
template<typename TypeIter>
DictValue DictValue::arrayReal(TypeIter begin, int size)
{
DictValue res(Param::REAL, new AutoBuffer<double, 1>(size));
for (int j = 0; j < size; begin++, j++)
(*res.pd)[j] = *begin;
return res;
}
template<typename TypeIter>
DictValue DictValue::arrayString(TypeIter begin, int size)
{
DictValue res(Param::STRING, new AutoBuffer<String, 1>(size));
for (int j = 0; j < size; begin++, j++)
(*res.ps)[j] = *begin;
return res;
}
/** @brief This class implements name-value dictionary, values are instances of DictValue. */
class CV_EXPORTS Dict
{
typedef std::map<String, DictValue> _Dict;
@ -131,276 +110,31 @@ class CV_EXPORTS Dict
public:
bool has(const String &name)
{
return dict.count(name) != 0;
}
//! Checks a presence of the @p key in the dictionary.
bool has(const String &key);
DictValue *ptr(const String &name)
{
_Dict::iterator i = dict.find(name);
return (i == dict.end()) ? NULL : &i->second;
}
//! If the @p key in the dictionary then returns pointer to its value, else returns NULL.
DictValue *ptr(const String &key);
const DictValue &get(const String &name) const
{
_Dict::const_iterator i = dict.find(name);
if (i == dict.end())
CV_Error(Error::StsBadArg, "Required argument \"" + name + "\" not found into dictionary");
return i->second;
}
//! If the @p key in the dictionary then returns its value, else an error will be generated.
const DictValue &get(const String &key) const;
/** @overload */
template <typename T>
T get(const String &name) const
{
return this->get(name).get<T>();
}
T get(const String &key) const;
//! If the @p key in the dictionary then returns its value, else returns @p defaultValue.
template <typename T>
T get(const String &name, const T &default_value) const
{
_Dict::const_iterator i = dict.find(name);
if (i != dict.end())
return i->second.get<T>();
else
return default_value;
}
T get(const String &key, const T &defaultValue) const;
//! Sets new @p value for the @p key, or adds new key-value pair into the dictionary.
template<typename T>
const T &set(const String &name, const T &value)
{
_Dict::iterator i = dict.find(name);
if (i != dict.end())
i->second = DictValue(value);
else
dict.insert(std::make_pair(name, DictValue(value)));
return value;
}
const T &set(const String &key, const T &value);
friend std::ostream &operator<<(std::ostream &stream, const Dict &dict);
};
template<>
inline DictValue DictValue::get<DictValue>(int idx) const
{
CV_Assert(idx == -1);
return *this;
}
template<>
inline int64 DictValue::get<int64>(int idx) const
{
CV_Assert(idx == -1 && size() == 1 || idx >= 0 && idx < size());
idx = (idx == -1) ? 0 : idx;
if (type == Param::INT)
{
return (*pi)[idx];
}
else if (type == Param::REAL)
{
double doubleValue = (*pd)[idx];
double fracpart, intpart;
fracpart = std::modf(doubleValue, &intpart);
CV_Assert(fracpart == 0.0);
return (int64)doubleValue;
}
else
{
CV_Assert(isInt() || isReal());
return 0;
}
}
template<>
inline int DictValue::get<int>(int idx) const
{
return (int)get<int64>(idx);
}
template<>
inline unsigned DictValue::get<unsigned>(int idx) const
{
return (unsigned)get<int64>(idx);
}
template<>
inline bool DictValue::get<bool>(int idx) const
{
return (get<int64>(idx) != 0);
}
template<>
inline double DictValue::get<double>(int idx) const
{
CV_Assert(idx == -1 && size() == 1 || idx >= 0 && idx < size());
idx = (idx == -1) ? 0 : idx;
if (type == Param::REAL)
{
return (*pd)[idx];
}
else if (type == Param::INT)
{
return (double)(*pi)[idx];
}
else
{
CV_Assert(isReal() || isInt());
return 0;
}
}
template<>
inline float DictValue::get<float>(int idx) const
{
return (float)get<double>(idx);
}
template<>
inline String DictValue::get<String>(int idx) const
{
CV_Assert(isString());
CV_Assert(idx == -1 && ps->size() == 1 || idx >= 0 && idx < (int)ps->size());
return (*ps)[(idx == -1) ? 0 : idx];
}
inline void DictValue::release()
{
switch (type)
{
case Param::INT:
delete pi;
break;
case Param::STRING:
delete ps;
break;
case Param::REAL:
delete pd;
break;
}
}
inline DictValue::~DictValue()
{
release();
}
inline DictValue & DictValue::operator=(const DictValue &r)
{
if (&r == this)
return *this;
if (r.type == Param::INT)
{
AutoBuffer<int64, 1> *tmp = new AutoBuffer<int64, 1>(*r.pi);
release();
pi = tmp;
}
else if (r.type == Param::STRING)
{
AutoBuffer<String, 1> *tmp = new AutoBuffer<String, 1>(*r.ps);
release();
ps = tmp;
}
else if (r.type == Param::REAL)
{
AutoBuffer<double, 1> *tmp = new AutoBuffer<double, 1>(*r.pd);
release();
pd = tmp;
}
type = r.type;
return *this;
}
inline DictValue::DictValue(const DictValue &r)
{
type = r.type;
if (r.type == Param::INT)
pi = new AutoBuffer<int64, 1>(*r.pi);
else if (r.type == Param::STRING)
ps = new AutoBuffer<String, 1>(*r.ps);
else if (r.type == Param::REAL)
pd = new AutoBuffer<double, 1>(*r.pd);
}
inline bool DictValue::isString() const
{
return (type == Param::STRING);
}
inline bool DictValue::isInt() const
{
return (type == Param::INT);
}
inline bool DictValue::isReal() const
{
return (type == Param::REAL || type == Param::INT);
}
inline int DictValue::size() const
{
switch (type)
{
case Param::INT:
return (int)pi->size();
break;
case Param::STRING:
return (int)ps->size();
break;
case Param::REAL:
return (int)pd->size();
break;
default:
CV_Error(Error::StsInternal, "");
return -1;
}
}
inline std::ostream &operator<<(std::ostream &stream, const DictValue &dictv)
{
int i;
if (dictv.isInt())
{
for (i = 0; i < dictv.size() - 1; i++)
stream << dictv.get<int64>(i) << ", ";
stream << dictv.get<int64>(i);
}
else if (dictv.isReal())
{
for (i = 0; i < dictv.size() - 1; i++)
stream << dictv.get<double>(i) << ", ";
stream << dictv.get<double>(i);
}
else if (dictv.isString())
{
for (i = 0; i < dictv.size() - 1; i++)
stream << "\"" << dictv.get<String>(i) << "\", ";
stream << dictv.get<String>(i);
}
return stream;
}
inline std::ostream &operator<<(std::ostream &stream, const Dict &dict)
{
Dict::_Dict::const_iterator it;
for (it = dict.dict.begin(); it != dict.dict.end(); it++)
stream << it->first << " : " << it->second << "\n";
return stream;
}
//! @}
}
}

@ -49,19 +49,23 @@
namespace cv
{
namespace dnn
namespace dnn //! This namespace is used for dnn module functionlaity.
{
//! @addtogroup dnn
//! @{
/** @brief Initialize dnn module and built-in layers.
*
* This function automatically called on most of OpenCV builds,
* but you need to call it manually on some specific configurations.
* but you need to call it manually on some specific configurations (iOS for example).
*/
CV_EXPORTS void initModule();
/** @brief
*
/** @brief This class provides all data needed to initialize layer.
*
* */
* It includes dictionary with scalar params (which can be readed by using Dict interface),
* blob params #blobs and optional meta information: #name and #type of layer instance.
*/
struct CV_EXPORTS LayerParams : public Dict
{
std::vector<Blob> blobs; //!< List of learned parameters stored as blobs.
@ -70,16 +74,15 @@ namespace dnn
String type; //!< Type name which was used for creating layer by layer factory (optional).
};
/** @brief Interface class allows to build new Layers.
*/
/** @brief This interface class allows to build new Layers - are building blocks of networks. */
struct CV_EXPORTS Layer
{
///List of learned parameters must be stored here to allow read them by using Net::getParam().
std::vector<Blob> blobs;
/** @brief Allocates internal buffers and output blobs with respect to the shape of inputs.
* @param[in] input vector of already allocated input blobs
* @param[out] output vector of output blobs, which must be allocated
* @param[in] input vector of already allocated input blobs
* @param[out] output vector of output blobs, which must be allocated
*
* This method must create each produced blob according to shape of @p input blobs and internal layer params.
* If this method is called first time then @p output vector consists from empty blobs and its size determined by number of output connections.
@ -90,14 +93,14 @@ namespace dnn
virtual void forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs) = 0;
/** @brief Returns index of input blob into the input array.
* @param inputName label of input blob
* @param inputName label of input blob
*
* Each input and output blob can be labeled to easily identify them using "<layer_name>[.output_name]" notation.
* This method map label of input blob to its index into input vector.
* Each layer input and output can be labeled to easily identify them using "%<layer_name%>[.output_name]" notation.
* This method maps label of input blob to its index into input vector.
*/
virtual int inputNameToIndex(String inputName);
/** @brief Returns index of output blob in output array.
* @see inputNameToIndex()
* @see inputNameToIndex()
*/
virtual int outputNameToIndex(String outputName);
@ -109,8 +112,15 @@ namespace dnn
virtual ~Layer();
};
typedef DictValue LayerId; //!< Container for strings and integers.
/** @brief This class allows to create and maunipulate comprehensive artifical neural networks.
*
* Neural network is presented as directed acyclic graph (DAG), where vertices are Layer instances,
* and edges specify relationships between layers inputs and ouputs.
*
* Each network layer has unique integer id and unique string name inside its network.
*
* This class supports reference counting of its instances, i. e. copies point to the same instance.
*/
class CV_EXPORTS Net
{
public:
@ -118,14 +128,57 @@ namespace dnn
Net();
~Net();
/** @brief Adds new layer to the net.
* @param name unique name of the adding layer.
* @param type typename of the adding layer (type must be registered in LayerRegister).
* @param params parameters which will be used to initialize the creating layer.
* @returns unique identifier of created layer, or -1 if a failure will happen.
*/
int addLayer(const String &name, const String &type, LayerParams &params);
/** @brief Adds new layer and connects its first input to the first output of previously added layer.
* @see addLayer()
*/
int addLayerToPrev(const String &name, const String &type, LayerParams &params);
/** @brief Converts string name of the layer to the integer identifier.
* @returns id of the layer, or -1 if the layer wasn't found.
*/
int getLayerId(const String &layer);
/** @brief Container for strings and integers. */
typedef DictValue LayerId;
/** @brief Delete layer for the network (not implemented yet) */
void deleteLayer(LayerId layer);
/** @brief Connects output of the first layer to input of the second layer.
* @param outPin descriptor of the first layer output.
* @param inpPin descriptor of the second layer input.
*
* Descriptors have the following template <DFN>&lt;layer_name&gt;[.input_number]</DFN>:
* - the first part of the tamplate <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 inLayerId, int inNum);
/** @brief Connects #@p outNum output of the first layer to #@p inNum input of the second layer.
* @param outLayerId identifier of the first layer
* @param inpLayerId identifier of the second layer
* @param outNum number of the first layer output
* @param inpNum number of the second layer input
*/
void connect(int outLayerId, int outNum, int inpLayerId, int inpNum);
/** @brief Sets ouputs names of the network input pseudo layer.
*
* Each net always has special own the network input pseudo layer with id=0.
* This layer stores the user blobs only and don't make any computations.
* In fact, this layer provides the only way to pass user data into the network.
* As any other layer, this layer can label its outputs and this function provides an easy way to do this.
*/
void setNetInputs(const std::vector<String> &inputBlobNames);
void forward();
@ -149,20 +202,57 @@ namespace dnn
Ptr<Impl> impl;
};
/** @brief Small interface class for loading trained serialized models of different dnn-frameworks. */
class Importer
{
public:
/** @brief Adds loaded layers into the @p net and sets connetions between them. */
virtual void populateNet(Net net) = 0;
virtual ~Importer();
};
/** @brief Creates the importer of <a href="http://caffe.berkeleyvision.org">Caffe</a> framework network.
* @param prototxt path to the .prototxt file with text description of the network architecture.
* @param caffeModel path to the .caffemodel file with learned network.
* @returns Pointer to the created importer, NULL in failure cases.
*/
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.
*/
CV_EXPORTS Ptr<Importer> createTorchImporter(const String &filename, bool isBinary = true);
CV_EXPORTS Blob readTorchMat(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);
//! @}
}
}

@ -48,7 +48,303 @@ namespace cv
{
namespace dnn
{
//code is absent ... today
template<typename TypeIter>
DictValue DictValue::arrayInt(TypeIter begin, int size)
{
DictValue res(Param::INT, new AutoBuffer<int64, 1>(size));
for (int j = 0; j < size; begin++, j++)
(*res.pi)[j] = *begin;
return res;
}
template<typename TypeIter>
DictValue DictValue::arrayReal(TypeIter begin, int size)
{
DictValue res(Param::REAL, new AutoBuffer<double, 1>(size));
for (int j = 0; j < size; begin++, j++)
(*res.pd)[j] = *begin;
return res;
}
template<typename TypeIter>
DictValue DictValue::arrayString(TypeIter begin, int size)
{
DictValue res(Param::STRING, new AutoBuffer<String, 1>(size));
for (int j = 0; j < size; begin++, j++)
(*res.ps)[j] = *begin;
return res;
}
template<>
inline DictValue DictValue::get<DictValue>(int idx) const
{
CV_Assert(idx == -1);
return *this;
}
template<>
inline int64 DictValue::get<int64>(int idx) const
{
CV_Assert(idx == -1 && size() == 1 || idx >= 0 && idx < size());
idx = (idx == -1) ? 0 : idx;
if (type == Param::INT)
{
return (*pi)[idx];
}
else if (type == Param::REAL)
{
double doubleValue = (*pd)[idx];
double fracpart, intpart;
fracpart = std::modf(doubleValue, &intpart);
CV_Assert(fracpart == 0.0);
return (int64)doubleValue;
}
else
{
CV_Assert(isInt() || isReal());
return 0;
}
}
template<>
inline int DictValue::get<int>(int idx) const
{
return (int)get<int64>(idx);
}
template<>
inline unsigned DictValue::get<unsigned>(int idx) const
{
return (unsigned)get<int64>(idx);
}
template<>
inline bool DictValue::get<bool>(int idx) const
{
return (get<int64>(idx) != 0);
}
template<>
inline double DictValue::get<double>(int idx) const
{
CV_Assert(idx == -1 && size() == 1 || idx >= 0 && idx < size());
idx = (idx == -1) ? 0 : idx;
if (type == Param::REAL)
{
return (*pd)[idx];
}
else if (type == Param::INT)
{
return (double)(*pi)[idx];
}
else
{
CV_Assert(isReal() || isInt());
return 0;
}
}
template<>
inline float DictValue::get<float>(int idx) const
{
return (float)get<double>(idx);
}
template<>
inline String DictValue::get<String>(int idx) const
{
CV_Assert(isString());
CV_Assert(idx == -1 && ps->size() == 1 || idx >= 0 && idx < (int)ps->size());
return (*ps)[(idx == -1) ? 0 : idx];
}
inline void DictValue::release()
{
switch (type)
{
case Param::INT:
delete pi;
break;
case Param::STRING:
delete ps;
break;
case Param::REAL:
delete pd;
break;
}
}
inline DictValue::~DictValue()
{
release();
}
inline DictValue & DictValue::operator=(const DictValue &r)
{
if (&r == this)
return *this;
if (r.type == Param::INT)
{
AutoBuffer<int64, 1> *tmp = new AutoBuffer<int64, 1>(*r.pi);
release();
pi = tmp;
}
else if (r.type == Param::STRING)
{
AutoBuffer<String, 1> *tmp = new AutoBuffer<String, 1>(*r.ps);
release();
ps = tmp;
}
else if (r.type == Param::REAL)
{
AutoBuffer<double, 1> *tmp = new AutoBuffer<double, 1>(*r.pd);
release();
pd = tmp;
}
type = r.type;
return *this;
}
inline DictValue::DictValue(const DictValue &r)
{
type = r.type;
if (r.type == Param::INT)
pi = new AutoBuffer<int64, 1>(*r.pi);
else if (r.type == Param::STRING)
ps = new AutoBuffer<String, 1>(*r.ps);
else if (r.type == Param::REAL)
pd = new AutoBuffer<double, 1>(*r.pd);
}
inline bool DictValue::isString() const
{
return (type == Param::STRING);
}
inline bool DictValue::isInt() const
{
return (type == Param::INT);
}
inline bool DictValue::isReal() const
{
return (type == Param::REAL || type == Param::INT);
}
inline int DictValue::size() const
{
switch (type)
{
case Param::INT:
return (int)pi->size();
break;
case Param::STRING:
return (int)ps->size();
break;
case Param::REAL:
return (int)pd->size();
break;
default:
CV_Error(Error::StsInternal, "");
return -1;
}
}
inline std::ostream &operator<<(std::ostream &stream, const DictValue &dictv)
{
int i;
if (dictv.isInt())
{
for (i = 0; i < dictv.size() - 1; i++)
stream << dictv.get<int64>(i) << ", ";
stream << dictv.get<int64>(i);
}
else if (dictv.isReal())
{
for (i = 0; i < dictv.size() - 1; i++)
stream << dictv.get<double>(i) << ", ";
stream << dictv.get<double>(i);
}
else if (dictv.isString())
{
for (i = 0; i < dictv.size() - 1; i++)
stream << "\"" << dictv.get<String>(i) << "\", ";
stream << dictv.get<String>(i);
}
return stream;
}
/////////////////////////////////////////////////////////////////
inline bool Dict::has(const String &key)
{
return dict.count(key) != 0;
}
inline DictValue *Dict::ptr(const String &key)
{
_Dict::iterator i = dict.find(key);
return (i == dict.end()) ? NULL : &i->second;
}
inline const DictValue &Dict::get(const String &key) const
{
_Dict::const_iterator i = dict.find(key);
if (i == dict.end())
CV_Error(Error::StsObjectNotFound, "Required argument \"" + key + "\" not found into dictionary");
return i->second;
}
template <typename T>
inline T Dict::get(const String &key) const
{
return this->get(key).get<T>();
}
template <typename T>
inline T Dict::get(const String &key, const T &defaultValue) const
{
_Dict::const_iterator i = dict.find(key);
if (i != dict.end())
return i->second.get<T>();
else
return defaultValue;
}
template<typename T>
inline const T &Dict::set(const String &key, const T &value)
{
_Dict::iterator i = dict.find(key);
if (i != dict.end())
i->second = DictValue(value);
else
dict.insert(std::make_pair(key, DictValue(value)));
return value;
}
inline std::ostream &operator<<(std::ostream &stream, const Dict &dict)
{
Dict::_Dict::const_iterator it;
for (it = dict.dict.begin(); it != dict.dict.end(); it++)
stream << it->first << " : " << it->second << "\n";
return stream;
}
}
}

@ -210,10 +210,10 @@ namespace
BlobShape shape = blobShapeFromProto(pbBlob);
dstBlob.create(shape, CV_32F);
CV_Assert(pbBlob.data_size() == (int)dstBlob.getMatRef().total());
CV_Assert(pbBlob.data_size() == (int)dstBlob.matRefConst().total());
CV_DbgAssert(pbBlob.GetDescriptor()->FindFieldByLowercaseName("data")->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT);
float *dstData = dstBlob.getMatRef().ptr<float>();
float *dstData = dstBlob.matRef().ptr<float>();
for (int i = 0; i < pbBlob.data_size(); i++)
dstData[i] = pbBlob.data(i);

@ -454,9 +454,9 @@ int Net::addLayerToPrev(const String &name, const String &type, LayerParams &par
return newLid;
}
void Net::connect(int outLayerId, int outNum, int inLayerId, int inNum)
void Net::connect(int outLayerId, int outNum, int inpLayerId, int inpNum)
{
impl->connect(outLayerId, outNum, inLayerId, inNum);
impl->connect(outLayerId, outNum, inpLayerId, inpNum);
}
void Net::connect(String _outPin, String _inPin)

@ -83,7 +83,7 @@ namespace dnn
void ConcatLayer::forward(std::vector<Blob *> &inputs, std::vector<Blob> &outputs)
{
const Mat& outMat = outputs[0].getMatRef();
const Mat& outMat = outputs[0].matRef();
std::vector<Range> ranges(outputs[0].dims(), Range::all());
int sizeStart = 0;
for (size_t i = 0; i < inputs.size(); i++)
@ -92,7 +92,7 @@ namespace dnn
ranges[axis] = Range(sizeStart, sizeEnd);
Mat outSubMat = outMat(&ranges[0]);
inputs[i]->getMatRef().copyTo(outSubMat);
inputs[i]->matRef().copyTo(outSubMat);
sizeStart = sizeEnd;
}

@ -159,7 +159,7 @@ namespace dnn
ranges[0] = Range(imNum, imNum+1);
ranges[1] = Range(cnGroup*inpGroupCn, (cnGroup + 1)*inpGroupCn);
UMat src = inpBlob.getMatRef()(&ranges[0]).getUMat(ACCESS_READ);
UMat src = inpBlob.matRef()(&ranges[0]).getUMat(ACCESS_READ);
UMat dst(colMat.size(), colMat.type());
im2col_ocl(src, inpGroupCn, inpH, inpW, kerH, kerW, padH, padW, strideH, strideW, dst);
dst.copyTo(colMat);

@ -77,8 +77,8 @@ void MVNLayer::forward(std::vector<Blob *> &inputs, std::vector<Blob> &outputs)
workSize[0] = (int)inpBlob.total(0, splitDim);
workSize[1] = (int)inpBlob.total(splitDim);
Mat inpMat = inpBlob.getMatRef().reshape(1, 2, workSize);
Mat outMat = outBlob.getMatRef().reshape(1, 2, workSize);
Mat inpMat = inpBlob.matRef().reshape(1, 2, workSize);
Mat outMat = outBlob.matRef().reshape(1, 2, workSize);
Scalar mean, dev;
for (int i = 0; i < workSize[0]; i++)

@ -110,7 +110,7 @@ void SliceLayer::forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs)
{
Blob &inpBlob = *inputs[0];
const int axis = inpBlob.canonicalAxis(inAxis);
const Mat& inpMat = inpBlob.getMatRef();
const Mat& inpMat = inpBlob.matRef();
std::vector<Range> ranges(inpBlob.dims(), Range::all());
int sizeStart = 0;
@ -120,7 +120,7 @@ void SliceLayer::forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs)
ranges[axis] = Range(sizeStart, sizeEnd);
Mat inpSubMat = inpMat(&ranges[0]);
inpSubMat.copyTo(outputs[i].getMatRef());
inpSubMat.copyTo(outputs[i].matRef());
sizeStart = sizeEnd;
}

@ -114,7 +114,7 @@ namespace dnn
}
}
cv::exp(dst.getMatRef(), dst.getMatRef());
cv::exp(dst.matRef(), dst.matRef());
for (size_t outerDim = 0; outerDim < outerSize; outerDim++)
{

@ -76,7 +76,7 @@ void SplitLayer::allocate(const std::vector<Blob*> &inputs, std::vector<Blob> &o
void SplitLayer::forward(std::vector<Blob*> &inputs, std::vector<Blob> &outputs)
{
for (size_t i = 0; i < outputs.size(); i++)
inputs[0]->getMatRef().copyTo(outputs[i].getMatRef());
inputs[0]->matRefConst().copyTo(outputs[i].matRef());
}
}

@ -392,7 +392,7 @@ struct TorchImporter : public ::cv::dnn::Importer
Blob blob;
blob.create(BlobShape(ndims, isizes), dstType);
srcMat.convertTo(blob.getMatRef(), dstType);
srcMat.convertTo(blob.matRef(), dstType);
tensors.insert(std::make_pair(indexTensor, blob));
}
@ -686,7 +686,7 @@ CV_EXPORTS Ptr<Importer> createTorchImporter(const String &filename, bool isBina
}
CV_EXPORTS Blob readTorchMat(const String &filename, bool isBinary)
CV_EXPORTS Blob readTorchBlob(const String &filename, bool isBinary)
{
Ptr<TorchImporter> importer(new TorchImporter(filename, isBinary));
importer->readObject();

@ -59,7 +59,7 @@ inline void normAssert(cv::InputArray ref, cv::InputArray test, const char *comm
inline void normAssert(cv::dnn::Blob &ref, cv::dnn::Blob &test, const char *comment = "")
{
ASSERT_EQ(ref.shape(), test.shape()) << comment;
normAssert(ref.getMatRef(), test.getMatRef(), comment);
normAssert(ref.matRefConst(), test.matRefConst(), comment);
}
#endif

@ -165,7 +165,7 @@ TEST(Layer_Test_Reshape_Split_Slice, Accuracy)
Blob input(BlobShape(Vec2i(6, 12)));
RNG rng(0);
rng.fill(input.getMatRef(), RNG::UNIFORM, -1, 1);
rng.fill(input.matRef(), RNG::UNIFORM, -1, 1);
net.setBlob(".input", input);
net.forward();

@ -77,8 +77,8 @@ static void runTorchNet(String prefix, String outLayerName, bool isBinary)
importer->populateNet(net);
Blob inp, outRef;
ASSERT_NO_THROW( inp = readTorchMat(_tf(prefix + "_input" + suffix), isBinary) );
ASSERT_NO_THROW( outRef = readTorchMat(_tf(prefix + "_output" + suffix), isBinary) );
ASSERT_NO_THROW( inp = readTorchBlob(_tf(prefix + "_input" + suffix), isBinary) );
ASSERT_NO_THROW( outRef = readTorchBlob(_tf(prefix + "_output" + suffix), isBinary) );
net.setBlob(".0", inp);
net.forward();

Loading…
Cancel
Save