From 85ad43d325565dd7523e4411c39597d5a247c347 Mon Sep 17 00:00:00 2001 From: Vitaliy Lyudvichenko Date: Mon, 6 Jul 2015 14:51:10 +0300 Subject: [PATCH] Generalized Blob constructor and added vector of images support. AlexNet and GoogLeNet tests updated. --- modules/dnn/include/opencv2/dnn/blob.hpp | 15 ++- modules/dnn/include/opencv2/dnn/blob.inl.hpp | 13 +++ modules/dnn/src/blob.cpp | 111 ++++++++++++++----- modules/dnn/src/dnn.cpp | 5 +- modules/dnn/test/test_alexnet.cpp | 41 ++----- modules/dnn/test/test_common.hpp | 24 ++++ modules/dnn/test/test_googlenet.cpp | 33 +----- modules/dnn/test/test_precomp.hpp | 1 + 8 files changed, 155 insertions(+), 88 deletions(-) create mode 100644 modules/dnn/test/test_common.hpp diff --git a/modules/dnn/include/opencv2/dnn/blob.hpp b/modules/dnn/include/opencv2/dnn/blob.hpp index 78cc7836e..929b709be 100644 --- a/modules/dnn/include/opencv2/dnn/blob.hpp +++ b/modules/dnn/include/opencv2/dnn/blob.hpp @@ -50,7 +50,11 @@ namespace dnn { public: explicit Blob(); - explicit Blob(InputArray in); + /** @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 + */ + explicit Blob(InputArray in, int dstCn = -1); void create(const BlobShape &shape, int type = CV_32F); @@ -111,6 +115,15 @@ namespace dnn template TFloat *ptr(int n = 0, int cn = 0, int row = 0, int col = 0); + /** @brief share data with other blob and returns *this + @returns *this + */ + Blob &shareFrom(const Blob &blob); + /** @brief adjust blob shape to required (data reallocated if needed) + @returns *this + */ + Blob &reshape(const BlobShape &shape); + int type() const; bool isFloat() const; bool isDouble() const; diff --git a/modules/dnn/include/opencv2/dnn/blob.inl.hpp b/modules/dnn/include/opencv2/dnn/blob.inl.hpp index 0ee571c61..4f5866a5f 100644 --- a/modules/dnn/include/opencv2/dnn/blob.inl.hpp +++ b/modules/dnn/include/opencv2/dnn/blob.inl.hpp @@ -254,6 +254,19 @@ inline const int * Blob::sizes() const return &m.size[0]; } + +inline Blob &Blob::shareFrom(const Blob &blob) +{ + this->m = blob.m; + return *this; +} + +inline Blob &Blob::reshape(const BlobShape &shape) +{ + m.reshape(1, shape.dims(), shape.ptr()); + return *this; +} + } } diff --git a/modules/dnn/src/blob.cpp b/modules/dnn/src/blob.cpp index 9f0c66660..e16f3b73f 100644 --- a/modules/dnn/src/blob.cpp +++ b/modules/dnn/src/blob.cpp @@ -11,45 +11,106 @@ namespace dnn m = Mat(4, zeros, CV_32F, NULL); } - Blob::Blob(InputArray in) + static inline int getMatChannels(const Mat &mat) { - CV_Assert(in.isMat() || in.isUMat()); + return (mat.dims <= 2) ? mat.channels() : mat.size[0]; + } - if (in.isMat()) - { - Mat mat = in.getMat(); + static BlobShape getBlobShpae(std::vector &vmat, int requestedCn = -1) + { + BlobShape shape(4); + int cnSum = 0, matCn; - CV_Assert(mat.dims == 2); - int rows = mat.rows; - int cols = mat.cols; - int cn = mat.channels(); - int type = mat.type(); - int dstType = CV_MAKE_TYPE(CV_MAT_DEPTH(type), 1); + CV_Assert(vmat.size() > 0); + + for (size_t i = 0; i < vmat.size(); i++) + { + Mat &mat = vmat[i]; + CV_Assert(!mat.empty()); + CV_Assert((mat.dims == 3 && mat.channels() == 1) || mat.dims <= 2); - this->create(BlobShape(1, cn, rows, cols), dstType); - uchar *data = m.data; - int step = rows * cols * CV_ELEM_SIZE(dstType); + matCn = getMatChannels(mat); + cnSum += getMatChannels(mat); - if (cn == 1) + if (i == 0) { - Mat wrapper2D(rows, cols, dstType, m.data); - mat.copyTo(wrapper2D); + shape[-1] = mat.cols; + shape[-2] = mat.rows; + shape[-3] = (requestedCn <= 0) ? matCn : requestedCn; } else { - std::vector wrappers(cn); - for (int i = 0; i < cn; i++) - { - wrappers[i] = Mat(rows, cols, dstType, data); - data += step; - } + if (mat.cols != shape[-1] || mat.rows != shape[-2]) + CV_Error(Error::StsError, "Each Mat.size() must be equal"); - cv::split(mat, wrappers); + if (requestedCn <= 0 && matCn != shape[-3]) + CV_Error(Error::StsError, "Each Mat.chnannels() (or number of planes) must be equal"); } } + + if (cnSum % shape[-3] != 0) + CV_Error(Error::StsError, "Total number of channels in vector is not a multiple of requsted channel number"); + + shape[0] = cnSum / shape[-3]; + return shape; + } + + static std::vector extractMatVector(InputArray in) + { + if (in.isMat() || in.isUMat()) + { + return std::vector(1, in.getMat()); + } + else if (in.isMatVector()) + { + return *static_cast*>(in.getObj()); + } + else if (in.isUMatVector()) + { + std::vector vmat; + in.getMatVector(vmat); + return vmat; + } else { - CV_Error(cv::Error::StsNotImplemented, "Not Implemented"); + CV_Assert(in.isMat() || in.isMatVector() || in.isUMat() || in.isUMatVector()); + return std::vector(); + } + } + + Blob::Blob(InputArray in, int dstCn) + { + CV_Assert(dstCn == -1 || dstCn > 0); + std::vector inMats = extractMatVector(in); + BlobShape dstShape = getBlobShpae(inMats, dstCn); + + m.create(dstShape.dims(), dstShape.ptr(), CV_32F); + + std::vector wrapBuf(dstShape[-3]); + int elemSize = m.elemSize(); + uchar *ptr = this->ptrRaw(); + for (size_t i = 0; i < inMats.size(); i++) + { + Mat inMat = inMats[i]; + + if (inMat.dims <= 2) + { + inMat.convertTo(inMat, m.type()); + + wrapBuf.resize(0); + for (int cn = 0; cn < inMat.channels(); cn++) + { + wrapBuf.push_back(Mat(inMat.rows, inMat.cols, m.type(), ptr)); + ptr += elemSize * inMat.total(); + } + + cv::split(inMat, wrapBuf); + } + else + { + inMat.convertTo(Mat(inMat.dims, inMat.size, m.type(), ptr), m.type()); + ptr += elemSize * inMat.total(); + } } } diff --git a/modules/dnn/src/dnn.cpp b/modules/dnn/src/dnn.cpp index 4ba7f1a4e..afa7f2e82 100644 --- a/modules/dnn/src/dnn.cpp +++ b/modules/dnn/src/dnn.cpp @@ -20,11 +20,10 @@ struct LayerOutId { int lid; int oid; - String name; LayerOutId() {} - LayerOutId(int layerId, int outputId, const String &outputName = String()) - : lid(layerId), oid(outputId), name(outputName) {} + LayerOutId(int layerId, int outputId) + : lid(layerId), oid(outputId) {} }; struct LayerData diff --git a/modules/dnn/test/test_alexnet.cpp b/modules/dnn/test/test_alexnet.cpp index bee5169e6..747edc1d5 100644 --- a/modules/dnn/test/test_alexnet.cpp +++ b/modules/dnn/test/test_alexnet.cpp @@ -9,32 +9,12 @@ using namespace testing; using namespace cv; using namespace cv::dnn; -static std::string getOpenCVExtraDir() -{ - return cvtest::TS::ptr()->get_data_path(); -} - -template -static std::string getTestFile(TStr filename) +template +static std::string getTestFile(TString filename) { return (getOpenCVExtraDir() + "/dnn/") + filename; } -inline void normAssert(InputArray ref, InputArray get, const char *comment = "") -{ - double normL1 = cvtest::norm(ref, get, NORM_L1)/ ref.getMat().total(); - EXPECT_LE(normL1, 0.0001) << comment; - - double normInf = cvtest::norm(ref, get, NORM_INF); - EXPECT_LE(normInf, 0.001) << comment; -} - -inline void normAssert(Blob &ref, Blob &test, const char *comment = "") -{ - EXPECT_EQ(ref.shape(), test.shape()); - normAssert(ref.getMatRef(), test.getMatRef(), comment); -} - TEST(Reproducibility_AlexNet, Accuracy) { Net net; @@ -44,20 +24,17 @@ TEST(Reproducibility_AlexNet, Accuracy) importer->populateNet(net); } - std::vector inpMats(2); - inpMats[0] = imread(getTestFile("alexnet_0.png")); - inpMats[1] = imread(getTestFile("alexnet_1.png")); + std::vector inpMats; + inpMats.push_back( imread(getTestFile("alexnet_0.png")) ); + inpMats.push_back( imread(getTestFile("alexnet_1.png")) ); ASSERT_TRUE(!inpMats[0].empty() && !inpMats[1].empty()); - inpMats[0].convertTo(inpMats[0], CV_32F); - Blob inp(inpMats[0]); - - net.setBlob("data", inp); - + net.setBlob("data", Blob(inpMats)); net.forward(); + Blob out = net.getBlob("prob"); - Blob ref = blobFromNPY(getTestFile("alexnet_prob.npy")); - normAssert(out, ref, "prob"); + Blob ref = blobFromNPY(getTestFile("alexnet.npy")); + normAssert(ref, out, "prob"); } } diff --git a/modules/dnn/test/test_common.hpp b/modules/dnn/test/test_common.hpp new file mode 100644 index 000000000..ff5834a7d --- /dev/null +++ b/modules/dnn/test/test_common.hpp @@ -0,0 +1,24 @@ +#ifndef __OPENCV_TEST_COMMON_HPP__ +#define __OPENCV_TEST_COMMON_HPP__ + +inline const std::string &getOpenCVExtraDir() +{ + return cvtest::TS::ptr()->get_data_path(); +} + +inline void normAssert(cv::InputArray ref, cv::InputArray get, const char *comment = "") +{ + double normL1 = cvtest::norm(ref, get, cv::NORM_L1)/ ref.getMat().total(); + EXPECT_NEAR(normL1, 0, 0.0001) << comment; + + double normInf = cvtest::norm(ref, get, cv::NORM_INF); + EXPECT_NEAR(normInf, 0, 0.001) << comment; +} + +inline void normAssert(cv::dnn::Blob &ref, cv::dnn::Blob &test, const char *comment = "") +{ + EXPECT_EQ(ref.shape(), test.shape()); + normAssert(ref.getMatRef(), test.getMatRef(), comment); +} + +#endif diff --git a/modules/dnn/test/test_googlenet.cpp b/modules/dnn/test/test_googlenet.cpp index 3f1ce92e6..0391abd6c 100644 --- a/modules/dnn/test/test_googlenet.cpp +++ b/modules/dnn/test/test_googlenet.cpp @@ -9,31 +9,12 @@ using namespace testing; using namespace cv; using namespace cv::dnn; -static std::string getOpenCVExtraDir() -{ - return cvtest::TS::ptr()->get_data_path(); -} - -template -static std::string getTestFile(TStr filename) +template +static std::string getTestFile(TString filename) { return (getOpenCVExtraDir() + "/dnn/") + filename; } -inline void normAssert(InputArray ref, InputArray get, const char *comment = "") -{ - double normL1 = cvtest::norm(ref, get, NORM_L1)/ ref.getMat().total(); - EXPECT_LE(normL1, 0.0001) << comment; - - double normInf = cvtest::norm(ref, get, NORM_INF); - EXPECT_LE(normInf, 0.001) << comment; -} - -inline void normAssert(Blob ref, Blob test, const char *comment = "") -{ - normAssert(ref.getMatRef(), test.getMatRef(), comment); -} - TEST(Reproducibility_GoogLeNet, Accuracy) { Net net; @@ -43,14 +24,12 @@ TEST(Reproducibility_GoogLeNet, Accuracy) importer->populateNet(net); } - std::vector inpMats(2); - inpMats[0] = imread(getTestFile("googlenet_0.png")); - inpMats[1] = imread(getTestFile("googlenet_1.png")); + std::vector inpMats; + inpMats.push_back( imread(getTestFile("googlenet_0.png")) ); + inpMats.push_back( imread(getTestFile("googlenet_1.png")) ); ASSERT_TRUE(!inpMats[0].empty() && !inpMats[1].empty()); - inpMats[0].convertTo(inpMats[0], CV_32F); - Blob inp(inpMats[0]); - + Blob inp(inpMats); net.setBlob("data", inp); net.forward(); diff --git a/modules/dnn/test/test_precomp.hpp b/modules/dnn/test/test_precomp.hpp index 7f54cb428..c8c38b49b 100644 --- a/modules/dnn/test/test_precomp.hpp +++ b/modules/dnn/test/test_precomp.hpp @@ -16,5 +16,6 @@ #include "opencv2/ts.hpp" #include #include +#include "test_common.hpp" #endif