diff --git a/modules/dnn/include/opencv2/dnn/blob.hpp b/modules/dnn/include/opencv2/dnn/blob.hpp index c07e9fa84..7a3ec151c 100644 --- a/modules/dnn/include/opencv2/dnn/blob.hpp +++ b/modules/dnn/include/opencv2/dnn/blob.hpp @@ -49,30 +49,50 @@ namespace cv { namespace 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); - BlobShape(int num, int cn, int rows, int cols); - BlobShape(int ndims, const int *sizes); - BlobShape(const std::vector &sizes); + 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); //!< Creates 4-dim shape [@p num, @p cn, @p rows, @p cols] + BlobShape(int ndims, const int *sizes); //!< Creates n-dim shape from the @p sizes array + BlobShape(const std::vector &sizes); //!< Creates n-dim shape from the @p sizes vector template - BlobShape(const Vec &shape); + BlobShape(const Vec &shape); //!< Creates n-dim shape from @ref cv::Vec + /** @brief Returns number of dimensions. */ int dims() const; - int size(int axis) const; + + /** @brief Returns reference to the size of the specified @p axis. + * + * Negative @p axis is supported, in this case a counting starts from the last axis, + * i. e. -1 corresponds to last axis. + * If non-existing axis was passed then an error will be generated. + */ int &size(int axis); - //do the same as size() - int operator[](int axis) const; - int &operator[](int axis); + /** @brief Returns the size of the specified @p axis. + * @see size() + */ + int size(int axis) const; + + int operator[](int axis) const; //!< Does the same thing as size(axis). + int &operator[](int axis); //!< Does the same thing as size(int) const. - //same as size(), but size of non-existing dimensions equal to 1 + /** @brief Returns the size of the specified @p axis. + * + * Does the same thing as size(int) const, but if non-existing axis will be passed then 1 will be returned, + * therefore this function always finishes successfuly. + */ int xsize(int axis) const; + /** @brief Returns the product of all sizes of axes. */ ptrdiff_t total(); + /** @brief Returns pointer to the first element of continuous size array. */ const int *ptr() const; + /** @brief Checks equality of two shapes. */ bool equal(const BlobShape &other) const; private: @@ -94,14 +114,16 @@ namespace dnn public: explicit Blob(); + /** @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 input - * @param in 2-dimensional or 3-dimensional single-channel image (or vector from them) - * @param dstCn if specified force size of ouptut blob channel-dimension + /** @brief Constucts 4-dimensional blob 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 */ - explicit Blob(InputArray in, int dstCn = -1); + 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); void fill(InputArray in); @@ -110,74 +132,85 @@ namespace dnn Mat& getMatRef(); const Mat& getMatRef() const; //TODO: add UMat get methods - Mat getMat(int n, int cn); - //shape getters - ///returns real count of blob dimensions + /** @brief Returns number of blob dimensions. */ int dims() const; - /** @brief returns size of corresponding dimension (axis) - @param axis dimension index - Python-like indexing is supported, so @p axis can be negative, i. e. -1 is last dimension. - Supposed that size of non-existing dimensions equal to 1, so the method always finished. - */ - int xsize(int axis) const; - - /** @brief returns size of corresponding dimension (axis) - @param axis dimension index - Python-like indexing is supported, so @p axis can be negative, i. e. -1 is last dimension. - @note Unlike xsize(), if @p axis points to non-existing dimension then an error will be generated. - */ + /** @brief Returns the size of the specified @p axis. + * + * Negative @p axis is supported, in this case a counting starts from the last axis, + * i. e. -1 corresponds to last axis. + * If non-existing axis was passed then an error will be generated. + */ int size(int axis) const; - /** @brief returns number of elements - @param startAxis starting axis (inverse indexing can be used) - @param endAxis ending (excluded) axis - @see canonicalAxis() - */ - size_t total(int startAxis = 0, int endAxis = -1) const; + /** @brief Returns the size of the specified @p axis. + * + * Does the same thing as size(int) const, but if non-existing axis will be passed then 1 will be returned, + * therefore this function always finishes successfuly. + */ + int xsize(int axis) const; - /** @brief converts axis index to canonical format (where 0 <= axis < dims()) + /** @brief Computes the product of sizes of axes among the specified axes range [@p startAxis; @p endAxis). + * @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()) */ int canonicalAxis(int axis) const; - /** @brief returns shape of the blob - */ + /** @brief Returns shape of the blob. */ BlobShape shape() const; + /** @brief Checks equality of two blobs shapes. */ bool equalShape(const Blob &other) const; - //shape getters for 4-dim Blobs processing - int cols() const; - int rows() const; - int channels() const; - int num() const; - Size size2() const; - Vec4i shape4() const; - - //CPU data pointer functions + /** @brief Returns sclice of first two dimensions. + * @details The behavior is similar to the following numpy code: blob[n, cn, ...] + */ + Mat getPlane(int n, int cn); + + /** @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. + Size size2() const; //!< Returns cv::Size(cols(), rows()) + Vec4i shape4() const; //!< Returns shape of firt four blob axes. + /** @}*/ + + /** @addtogroup CPU pointer getters + * @{ + */ template size_t offset(const Vec &pos) const; size_t offset(int n = 0, int cn = 0, int row = 0, int col = 0) const; uchar *ptr(int n = 0, int cn = 0, int row = 0, int col = 0); template TFloat *ptr(int n = 0, int cn = 0, int row = 0, int col = 0); + /** Returns (float*) ptr() */ float *ptrf(int n = 0, int cn = 0, int row = 0, int col = 0); //TODO: add const ptr methods + /** @}*/ - /** @brief Share data with other blob. - @returns *this - */ + /** @brief Shares data from other @p blob. + * @returns *this + */ Blob &shareFrom(const Blob &blob); - /** @brief Adjust blob shape to required (data reallocated if needed). - @returns *this - */ + /** @brief Changes shape of the blob without copying the data. + * @returns *this + */ Blob &reshape(const BlobShape &shape); + /** @brief Returns type of the blob. */ int type() const; - bool isFloat() const; - bool isDouble() const; private: const int *sizes() const; diff --git a/modules/dnn/include/opencv2/dnn/blob.inl.hpp b/modules/dnn/include/opencv2/dnn/blob.inl.hpp index 3fa3af1ef..818610ed0 100644 --- a/modules/dnn/include/opencv2/dnn/blob.inl.hpp +++ b/modules/dnn/include/opencv2/dnn/blob.inl.hpp @@ -187,12 +187,14 @@ inline size_t Blob::total(int startAxis, int endAxis) const if (startAxis < 0) startAxis += dims(); - if (endAxis == -1) + if (endAxis == INT_MAX) endAxis = dims(); + else if (endAxis < 0) + endAxis += dims(); CV_Assert(0 <= startAxis && startAxis <= endAxis && endAxis <= dims()); - size_t size = 1; //assume that blob isn't empty + size_t size = 1; //fix: assume that slice isn't empty for (int i = startAxis; i < endAxis; i++) size *= (size_t)sizes()[i]; @@ -266,9 +268,10 @@ inline const Mat& Blob::getMatRef() const return m; } -inline Mat Blob::getMat(int n, int cn) +inline Mat Blob::getPlane(int n, int cn) { - return Mat(rows(), cols(), m.type(), this->ptr(n, cn)); + CV_Assert(dims() > 2); + return Mat(dims() - 2, sizes() + 2, type(), ptr(n, cn)); } inline int Blob::cols() const @@ -301,16 +304,6 @@ inline int Blob::type() const return m.depth(); } -inline bool Blob::isFloat() const -{ - return (type() == CV_32F); -} - -inline bool Blob::isDouble() const -{ - return (type() == CV_32F); -} - inline const int * Blob::sizes() const { return &m.size[0]; diff --git a/modules/dnn/include/opencv2/dnn/dnn.hpp b/modules/dnn/include/opencv2/dnn/dnn.hpp index 7839b8fa0..c586a0ef9 100644 --- a/modules/dnn/include/opencv2/dnn/dnn.hpp +++ b/modules/dnn/include/opencv2/dnn/dnn.hpp @@ -78,8 +78,8 @@ namespace dnn std::vector blobs; /** @brief Allocates internal buffers and output blobs with respect to the shape of inputs. - * @param[in] inputs vector of already allocated input blobs - * @param[out] outputs 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. diff --git a/modules/dnn/src/blob.cpp b/modules/dnn/src/blob.cpp index f569dcf06..1ef47e082 100644 --- a/modules/dnn/src/blob.cpp +++ b/modules/dnn/src/blob.cpp @@ -119,10 +119,10 @@ namespace dnn } } - Blob::Blob(InputArray in, int dstCn) + Blob::Blob(InputArray image, int dstCn) { CV_Assert(dstCn == -1 || dstCn > 0); - std::vector inMats = extractMatVector(in); + std::vector inMats = extractMatVector(image); BlobShape dstShape = getBlobShpae(inMats, dstCn); m.create(dstShape.dims(), dstShape.ptr(), CV_32F); diff --git a/modules/dnn/src/layers/elementwise_layers.hpp b/modules/dnn/src/layers/elementwise_layers.hpp index 75f2f1a7a..e66066189 100644 --- a/modules/dnn/src/layers/elementwise_layers.hpp +++ b/modules/dnn/src/layers/elementwise_layers.hpp @@ -78,13 +78,13 @@ using std::pow; size_t size = outputs[i].total(); - if (outputs[i].isFloat()) + if (outputs[i].type() == CV_32F) { float *data = outputs[i].ptrf(); for (size_t j = 0; j < size; j++) data[j] = func(data[j]); } - else if (outputs[i].isDouble()) + else if (outputs[i].type() == CV_64F) { double *data = outputs[i].ptr(); for (size_t j = 0; j < size; j++) diff --git a/modules/dnn/src/layers/lrn_layer.cpp b/modules/dnn/src/layers/lrn_layer.cpp index e6f245725..a55d8fe01 100644 --- a/modules/dnn/src/layers/lrn_layer.cpp +++ b/modules/dnn/src/layers/lrn_layer.cpp @@ -108,29 +108,29 @@ namespace dnn for (int n = 0; n < num; n++) { - Mat accum = dstBlob.getMat(n, channels-1); //trick for memory saving + Mat accum = dstBlob.getPlane(n, channels-1); //trick for memory saving accum.setTo(0); for (int cn = 0; cn < std::min(ksize, channels); cn++) - cv::accumulateSquare(srcBlob.getMat(n, cn), accum); + cv::accumulateSquare(srcBlob.getPlane(n, cn), accum); for (int cn = 0; cn < channels; cn++) { if (cn + ksize < channels) { - cv::accumulateSquare(srcBlob.getMat(n, cn + ksize), accum); + cv::accumulateSquare(srcBlob.getPlane(n, cn + ksize), accum); } if (cn - ksize - 1 >= 0) { - Mat left = srcBlob.getMat(n, cn - ksize - 1); + Mat left = srcBlob.getPlane(n, cn - ksize - 1); cv::subtract(accum, left.mul(left), accum); //subtractSquare } - Mat dst = dstBlob.getMat(n, cn); + Mat dst = dstBlob.getPlane(n, cn); accum.convertTo(dst, dst.type(), alpha/size, 1); cv::pow(dst, beta, dst); - cv::divide(srcBlob.getMat(n, cn), dst, dst); + cv::divide(srcBlob.getPlane(n, cn), dst, dst); } } } @@ -144,11 +144,11 @@ namespace dnn { for (int cn = 0; cn < channels; cn++) { - Mat src = srcBlob.getMat(n, cn); - Mat dst = dstBlob.getMat(n, cn); + Mat src = srcBlob.getPlane(n, cn); + Mat dst = dstBlob.getPlane(n, cn); uchar *dataDst0 = dst.data; - cv::pow(srcBlob.getMat(n, cn), 2, dst); + cv::pow(srcBlob.getPlane(n, cn), 2, dst); //TODO: check border type cv::boxFilter(dst, dst, dst.depth(), cv::Size(size, size), cv::Point(-1, -1), false, cv::BORDER_CONSTANT); dst.convertTo(dst, dst.type(), alpha/(size*size), 1); diff --git a/modules/dnn/src/layers/softmax_layer.cpp b/modules/dnn/src/layers/softmax_layer.cpp index 7aade6237..6e9e9510c 100644 --- a/modules/dnn/src/layers/softmax_layer.cpp +++ b/modules/dnn/src/layers/softmax_layer.cpp @@ -81,7 +81,7 @@ namespace dnn size_t outerSize = src.total(0, axis); size_t channels = src.size(axis); - size_t innerSize = src.total(axis + 1, -1); + size_t innerSize = src.total(axis + 1); size_t outerStep = src.total(axis); size_t cnStep = src.total(axis + 1);