mirror of
https://github.com/opencv/opencv.git
synced 2026-07-31 00:03:03 +04:00
dnn: move module from opencv_contrib
https://github.com/opencv/opencv_contrib/tree/e6f63c7a38ca40c5dc33e38736e3027e3528d6cb/modules/dnn
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,382 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
using namespace cv;
|
||||
using namespace cv::dnn;
|
||||
|
||||
#if HAVE_PROTOBUF
|
||||
#include "caffe.pb.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <google/protobuf/message.h>
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
#include "caffe_io.hpp"
|
||||
|
||||
using ::google::protobuf::RepeatedField;
|
||||
using ::google::protobuf::RepeatedPtrField;
|
||||
using ::google::protobuf::Message;
|
||||
using ::google::protobuf::Descriptor;
|
||||
using ::google::protobuf::FieldDescriptor;
|
||||
using ::google::protobuf::Reflection;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
static cv::String toString(const T &v)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << v;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
class CaffeImporter : public Importer
|
||||
{
|
||||
caffe::NetParameter net;
|
||||
caffe::NetParameter netBinary;
|
||||
|
||||
public:
|
||||
|
||||
CaffeImporter(const char *pototxt, const char *caffeModel)
|
||||
{
|
||||
ReadNetParamsFromTextFileOrDie(pototxt, &net);
|
||||
|
||||
if (caffeModel && caffeModel[0])
|
||||
ReadNetParamsFromBinaryFileOrDie(caffeModel, &netBinary);
|
||||
}
|
||||
|
||||
void addParam(const Message &msg, const FieldDescriptor *field, cv::dnn::LayerParams ¶ms)
|
||||
{
|
||||
const Reflection *refl = msg.GetReflection();
|
||||
int type = field->cpp_type();
|
||||
bool isRepeated = field->is_repeated();
|
||||
const std::string &name = field->name();
|
||||
|
||||
#define SET_UP_FILED(getter, arrayConstr, gtype) \
|
||||
if (isRepeated) { \
|
||||
const RepeatedField<gtype> &v = refl->GetRepeatedField<gtype>(msg, field); \
|
||||
params.set(name, DictValue::arrayConstr(v.begin(), (int)v.size())); \
|
||||
} \
|
||||
else { \
|
||||
params.set(name, refl->getter(msg, field)); \
|
||||
}
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FieldDescriptor::CPPTYPE_INT32:
|
||||
SET_UP_FILED(GetInt32, arrayInt, ::google::protobuf::int32);
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_UINT32:
|
||||
SET_UP_FILED(GetUInt32, arrayInt, ::google::protobuf::uint32);
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_INT64:
|
||||
SET_UP_FILED(GetInt32, arrayInt, ::google::protobuf::int64);
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_UINT64:
|
||||
SET_UP_FILED(GetUInt32, arrayInt, ::google::protobuf::uint64);
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_BOOL:
|
||||
SET_UP_FILED(GetBool, arrayInt, bool);
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_DOUBLE:
|
||||
SET_UP_FILED(GetDouble, arrayReal, double);
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_FLOAT:
|
||||
SET_UP_FILED(GetFloat, arrayReal, float);
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_STRING:
|
||||
if (isRepeated) {
|
||||
const RepeatedPtrField<std::string> &v = refl->GetRepeatedPtrField<std::string>(msg, field);
|
||||
params.set(name, DictValue::arrayString(v.begin(), (int)v.size()));
|
||||
}
|
||||
else {
|
||||
params.set(name, refl->GetString(msg, field));
|
||||
}
|
||||
break;
|
||||
case FieldDescriptor::CPPTYPE_ENUM:
|
||||
if (isRepeated) {
|
||||
int size = refl->FieldSize(msg, field);
|
||||
std::vector<cv::String> buf(size);
|
||||
for (int i = 0; i < size; i++)
|
||||
buf[i] = refl->GetRepeatedEnum(msg, field, i)->name();
|
||||
params.set(name, DictValue::arrayString(buf.begin(), size));
|
||||
}
|
||||
else {
|
||||
params.set(name, refl->GetEnum(msg, field)->name());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsError, "Unknown type \"" + String(field->type_name()) + "\" in prototxt");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline static bool ends_with_param(const std::string &str)
|
||||
{
|
||||
static const std::string _param("_param");
|
||||
return (str.size() >= _param.size()) && str.compare(str.size() - _param.size(), _param.size(), _param) == 0;
|
||||
}
|
||||
|
||||
void extractLayerParams(const Message &msg, cv::dnn::LayerParams ¶ms, bool isInternal = false)
|
||||
{
|
||||
const Descriptor *msgDesc = msg.GetDescriptor();
|
||||
const Reflection *msgRefl = msg.GetReflection();
|
||||
|
||||
for (int fieldId = 0; fieldId < msgDesc->field_count(); fieldId++)
|
||||
{
|
||||
const FieldDescriptor *fd = msgDesc->field(fieldId);
|
||||
|
||||
if (!isInternal && !ends_with_param(fd->name()))
|
||||
continue;
|
||||
|
||||
bool hasData = fd->is_required() ||
|
||||
(fd->is_optional() && msgRefl->HasField(msg, fd)) ||
|
||||
(fd->is_repeated() && msgRefl->FieldSize(msg, fd) > 0);
|
||||
if (!hasData)
|
||||
continue;
|
||||
|
||||
if (fd->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE)
|
||||
{
|
||||
if (fd->is_repeated()) //Extract only first item!
|
||||
extractLayerParams(msgRefl->GetRepeatedMessage(msg, fd, 0), params, true);
|
||||
else
|
||||
extractLayerParams(msgRefl->GetMessage(msg, fd), params, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
addParam(msg, fd, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void blobShapeFromProto(const caffe::BlobProto &pbBlob, MatShape& shape)
|
||||
{
|
||||
shape.clear();
|
||||
if (pbBlob.has_num() || pbBlob.has_channels() || pbBlob.has_height() || pbBlob.has_width())
|
||||
{
|
||||
shape.push_back(pbBlob.num());
|
||||
shape.push_back(pbBlob.channels());
|
||||
shape.push_back(pbBlob.height());
|
||||
shape.push_back(pbBlob.width());
|
||||
}
|
||||
else if (pbBlob.has_shape())
|
||||
{
|
||||
const caffe::BlobShape &_shape = pbBlob.shape();
|
||||
|
||||
for (int i = 0; i < _shape.dim_size(); i++)
|
||||
shape.push_back((int)_shape.dim(i));
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsError, "Unknown shape of input blob");
|
||||
}
|
||||
|
||||
void blobFromProto(const caffe::BlobProto &pbBlob, cv::Mat &dstBlob)
|
||||
{
|
||||
MatShape shape;
|
||||
blobShapeFromProto(pbBlob, shape);
|
||||
|
||||
dstBlob.create((int)shape.size(), &shape[0], CV_32F);
|
||||
CV_Assert(pbBlob.data_size() == (int)dstBlob.total());
|
||||
|
||||
CV_DbgAssert(pbBlob.GetDescriptor()->FindFieldByLowercaseName("data")->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT);
|
||||
float *dstData = dstBlob.ptr<float>();
|
||||
|
||||
for (int i = 0; i < pbBlob.data_size(); i++)
|
||||
dstData[i] = pbBlob.data(i);
|
||||
}
|
||||
|
||||
void extractBinaryLayerParms(const caffe::LayerParameter& layer, LayerParams& layerParams)
|
||||
{
|
||||
const std::string &name = layer.name();
|
||||
|
||||
int li;
|
||||
for (li = 0; li != netBinary.layer_size(); li++)
|
||||
{
|
||||
if (netBinary.layer(li).name() == name)
|
||||
break;
|
||||
}
|
||||
|
||||
if (li == netBinary.layer_size() || netBinary.layer(li).blobs_size() == 0)
|
||||
return;
|
||||
|
||||
const caffe::LayerParameter &binLayer = netBinary.layer(li);
|
||||
layerParams.blobs.resize(binLayer.blobs_size());
|
||||
for (int bi = 0; bi < binLayer.blobs_size(); bi++)
|
||||
{
|
||||
blobFromProto(binLayer.blobs(bi), layerParams.blobs[bi]);
|
||||
}
|
||||
}
|
||||
|
||||
struct BlobNote
|
||||
{
|
||||
BlobNote(const std::string &_name, int _layerId, int _outNum) :
|
||||
name(_name.c_str()), layerId(_layerId), outNum(_outNum) {}
|
||||
|
||||
const char *name;
|
||||
int layerId, outNum;
|
||||
};
|
||||
|
||||
std::vector<BlobNote> addedBlobs;
|
||||
std::map<String, int> layerCounter;
|
||||
|
||||
void populateNet(Net dstNet)
|
||||
{
|
||||
int layersSize = net.layer_size();
|
||||
layerCounter.clear();
|
||||
addedBlobs.clear();
|
||||
addedBlobs.reserve(layersSize + 1);
|
||||
|
||||
//setup input layer names
|
||||
{
|
||||
std::vector<String> netInputs(net.input_size());
|
||||
for (int inNum = 0; inNum < net.input_size(); inNum++)
|
||||
{
|
||||
addedBlobs.push_back(BlobNote(net.input(inNum), 0, inNum));
|
||||
netInputs[inNum] = net.input(inNum);
|
||||
}
|
||||
dstNet.setInputsNames(netInputs);
|
||||
}
|
||||
|
||||
for (int li = 0; li < layersSize; li++)
|
||||
{
|
||||
const caffe::LayerParameter &layer = net.layer(li);
|
||||
String name = layer.name();
|
||||
String type = layer.type();
|
||||
LayerParams layerParams;
|
||||
|
||||
extractLayerParams(layer, layerParams);
|
||||
extractBinaryLayerParms(layer, layerParams);
|
||||
|
||||
int repetitions = layerCounter[name]++;
|
||||
if (repetitions)
|
||||
name += String("_") + toString(repetitions);
|
||||
|
||||
int id = dstNet.addLayer(name, type, layerParams);
|
||||
|
||||
for (int inNum = 0; inNum < layer.bottom_size(); inNum++)
|
||||
addInput(layer.bottom(inNum), id, inNum, dstNet);
|
||||
|
||||
for (int outNum = 0; outNum < layer.top_size(); outNum++)
|
||||
addOutput(layer, id, outNum);
|
||||
}
|
||||
|
||||
addedBlobs.clear();
|
||||
}
|
||||
|
||||
void addOutput(const caffe::LayerParameter &layer, int layerId, int outNum)
|
||||
{
|
||||
const std::string &name = layer.top(outNum);
|
||||
|
||||
bool haveDups = false;
|
||||
for (int idx = (int)addedBlobs.size() - 1; idx >= 0; idx--)
|
||||
{
|
||||
if (addedBlobs[idx].name == name)
|
||||
{
|
||||
haveDups = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (haveDups)
|
||||
{
|
||||
bool isInplace = layer.bottom_size() > outNum && layer.bottom(outNum) == name;
|
||||
if (!isInplace)
|
||||
CV_Error(Error::StsBadArg, "Duplicate blobs produced by multiple sources");
|
||||
}
|
||||
|
||||
addedBlobs.push_back(BlobNote(name, layerId, outNum));
|
||||
}
|
||||
|
||||
void addInput(const std::string &name, int layerId, int inNum, Net &dstNet)
|
||||
{
|
||||
int idx;
|
||||
for (idx = (int)addedBlobs.size() - 1; idx >= 0; idx--)
|
||||
{
|
||||
if (addedBlobs[idx].name == name)
|
||||
break;
|
||||
}
|
||||
|
||||
if (idx < 0)
|
||||
{
|
||||
CV_Error(Error::StsObjectNotFound, "Can't find output blob \"" + name + "\"");
|
||||
return;
|
||||
}
|
||||
|
||||
dstNet.connect(addedBlobs[idx].layerId, addedBlobs[idx].outNum, layerId, inNum);
|
||||
}
|
||||
|
||||
~CaffeImporter()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
Ptr<Importer> cv::dnn::createCaffeImporter(const String &prototxt, const String &caffeModel)
|
||||
{
|
||||
return Ptr<Importer>(new CaffeImporter(prototxt.c_str(), caffeModel.c_str()));
|
||||
}
|
||||
|
||||
#else //HAVE_PROTOBUF
|
||||
|
||||
Ptr<Importer> cv::dnn::createCaffeImporter(const String&, const String&)
|
||||
{
|
||||
CV_Error(cv::Error::StsNotImplemented, "libprotobuf required to import data from Caffe models");
|
||||
return Ptr<Importer>();
|
||||
}
|
||||
|
||||
#endif //HAVE_PROTOBUF
|
||||
|
||||
Net cv::dnn::readNetFromCaffe(const String &prototxt, const String &caffeModel /*= String()*/)
|
||||
{
|
||||
Ptr<Importer> caffeImporter = createCaffeImporter(prototxt, caffeModel);
|
||||
Net net;
|
||||
if (caffeImporter)
|
||||
caffeImporter->populateNet(net);
|
||||
return net;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,108 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//COPYRIGHT
|
||||
//
|
||||
//All contributions by the University of California:
|
||||
//Copyright (c) 2014, The Regents of the University of California (Regents)
|
||||
//All rights reserved.
|
||||
//
|
||||
//All other contributions:
|
||||
//Copyright (c) 2014, the respective contributors
|
||||
//All rights reserved.
|
||||
//
|
||||
//Caffe uses a shared copyright model: each contributor holds copyright over
|
||||
//their contributions to Caffe. The project versioning records all such
|
||||
//contribution and copyright details. If a contributor wants to further mark
|
||||
//their specific copyright on a particular contribution, they should indicate
|
||||
//their copyright solely in the commit message of the change when it is
|
||||
//committed.
|
||||
//
|
||||
//LICENSE
|
||||
//
|
||||
//Redistribution and use in source and binary forms, with or without
|
||||
//modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
//1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
//2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
//WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
//DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
//ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
//(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
//ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
//(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
//SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
//
|
||||
//CONTRIBUTION AGREEMENT
|
||||
//
|
||||
//By contributing to the BVLC/caffe repository through pull-request, comment,
|
||||
//or otherwise, the contributor releases their content to the
|
||||
//license and copyright terms herein.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_DNN_CAFFE_IO_HPP__
|
||||
#define __OPENCV_DNN_CAFFE_IO_HPP__
|
||||
#if HAVE_PROTOBUF
|
||||
|
||||
#include "caffe.pb.h"
|
||||
|
||||
namespace cv {
|
||||
namespace dnn {
|
||||
|
||||
// Read parameters from a file into a NetParameter proto message.
|
||||
void ReadNetParamsFromTextFileOrDie(const char* param_file,
|
||||
caffe::NetParameter* param);
|
||||
void ReadNetParamsFromBinaryFileOrDie(const char* param_file,
|
||||
caffe::NetParameter* param);
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_DNN_CAFFE_GLOG_EMULATOR_HPP__
|
||||
#define __OPENCV_DNN_CAFFE_GLOG_EMULATOR_HPP__
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#define CHECK(cond) for(cv::dnn::GLogWrapper _logger(__FILE__, CV_Func, __LINE__, "CHECK", #cond, cond); _logger.exit(); _logger.check()) _logger.stream()
|
||||
#define CHECK_EQ(a, b) for(cv::dnn::GLogWrapper _logger(__FILE__, CV_Func, __LINE__, "CHECK", #a"="#b, ((a) == (b))); _logger.exit(); _logger.check()) _logger.stream()
|
||||
#define LOG(TYPE) for(cv::dnn::GLogWrapper _logger(__FILE__, CV_Func, __LINE__, #TYPE); _logger.exit(); _logger.check()) _logger.stream()
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class GLogWrapper
|
||||
{
|
||||
const char *file, *func, *type, *cond_str;
|
||||
int line;
|
||||
bool cond_staus, exit_loop;
|
||||
std::stringstream sstream;
|
||||
|
||||
public:
|
||||
|
||||
GLogWrapper(const char *_file, const char *_func, int _line,
|
||||
const char *_type,
|
||||
const char *_cond_str = NULL, bool _cond_status = true
|
||||
) :
|
||||
file(_file), func(_func), type(_type), cond_str(_cond_str),
|
||||
line(_line), cond_staus(_cond_status), exit_loop(true) {}
|
||||
|
||||
std::iostream &stream()
|
||||
{
|
||||
return sstream;
|
||||
}
|
||||
|
||||
bool exit()
|
||||
{
|
||||
return exit_loop;
|
||||
}
|
||||
|
||||
void check()
|
||||
{
|
||||
exit_loop = false;
|
||||
|
||||
if (cond_str && !cond_staus)
|
||||
{
|
||||
cv::error(cv::Error::StsError, "FAILED: " + String(cond_str) + ". " + sstream.str(), func, file, line);
|
||||
}
|
||||
else if (!cond_str && strcmp(type, "CHECK"))
|
||||
{
|
||||
if (!std::strcmp(type, "INFO"))
|
||||
std::cout << sstream.str() << std::endl;
|
||||
else
|
||||
std::cerr << sstream.str() << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,284 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2017, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#include "halide_scheduler.hpp"
|
||||
#include "op_halide.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
static void applySplit(const FileNode& directive, Halide::Func& func,
|
||||
const FileNode& params)
|
||||
{
|
||||
for (const auto& varNode : directive)
|
||||
{
|
||||
const std::string varName = varNode.name();
|
||||
const std::string factorName = (std::string)varNode;
|
||||
Halide::Var var(varName);
|
||||
Halide::Var outerVar(varName + "o");
|
||||
Halide::Var innerVar(varName + "i");
|
||||
// If split factor is integer or parameters map has parameter value.
|
||||
CV_Assert(varNode.isString() && !params[factorName].empty() ||
|
||||
varNode.isInt());
|
||||
int factor = (int)(varNode.isInt() ? varNode : params[factorName]);
|
||||
func.split(var, outerVar, innerVar, factor);
|
||||
}
|
||||
}
|
||||
|
||||
static void applyReorder(const FileNode& directive, Halide::Func& func)
|
||||
{
|
||||
std::string varName;
|
||||
const int numVars = directive.size();
|
||||
std::vector<Halide::VarOrRVar> reorderedVars;
|
||||
reorderedVars.reserve(numVars);
|
||||
for (int i = 0; i < numVars; ++i)
|
||||
{
|
||||
directive[i] >> varName;
|
||||
reorderedVars.push_back(Halide::Var(varName));
|
||||
}
|
||||
func.reorder(reorderedVars);
|
||||
}
|
||||
|
||||
static void applyFuse(const FileNode& directive, Halide::Func& func)
|
||||
{
|
||||
CV_Assert(directive["src"].size() >= 2);
|
||||
CV_Assert(directive["dst"].size() == 1);
|
||||
|
||||
std::string str;
|
||||
directive["src"][0] >> str;
|
||||
Halide::Var firstVar(str);
|
||||
directive["src"][1] >> str;
|
||||
Halide::Var secondVar(str);
|
||||
directive["dst"] >> str;
|
||||
Halide::Var dstVar(str);
|
||||
|
||||
func.fuse(firstVar, secondVar, dstVar);
|
||||
for (int i = 2, n = directive["src"].size(); i < n; ++i)
|
||||
{
|
||||
directive["src"][i] >> str;
|
||||
func.fuse(Halide::Var(str), dstVar, dstVar);
|
||||
}
|
||||
}
|
||||
|
||||
static void applyParallel(const FileNode& directive, Halide::Func& func)
|
||||
{
|
||||
std::string varName;
|
||||
for (int i = 0, n = directive.size(); i < n; ++i)
|
||||
{
|
||||
directive[i] >> varName;
|
||||
func.parallel(Halide::Var(varName));
|
||||
}
|
||||
}
|
||||
|
||||
static void applyUnroll(const FileNode& directive, Halide::Func& func)
|
||||
{
|
||||
std::string varName;
|
||||
for (int i = 0, n = directive.size(); i < n; ++i)
|
||||
{
|
||||
directive[i] >> varName;
|
||||
func.unroll(Halide::Var(varName));
|
||||
}
|
||||
}
|
||||
|
||||
static void applyVectorize(const FileNode& directive, Halide::Func& func,
|
||||
const FileNode& params)
|
||||
{
|
||||
for (const auto& varNode : directive)
|
||||
{
|
||||
const std::string varName = varNode.name();
|
||||
const std::string factorName = (std::string)varNode;
|
||||
// If split factor is integer or parameters map has parameter value.
|
||||
CV_Assert(varNode.isString() && !params[factorName].empty() ||
|
||||
varNode.isInt());
|
||||
int factor = (int)(varNode.isInt() ? varNode : params[factorName]);
|
||||
Halide::Var var(varName);
|
||||
Halide::Var inner(varName + "v");
|
||||
func.split(var, var, inner, factor);
|
||||
func.vectorize(inner);
|
||||
}
|
||||
}
|
||||
|
||||
static void applyStoreAt(const FileNode& directive, Halide::Func& func,
|
||||
std::map<std::string, Halide::Func>& funcsMap)
|
||||
{
|
||||
for (const auto& funcNode : directive)
|
||||
{
|
||||
const std::string targetFuncName = funcNode.name();
|
||||
if (funcsMap.find(targetFuncName) == funcsMap.end())
|
||||
CV_Error(cv::Error::StsParseError, "Function " + targetFuncName +
|
||||
" is not represented in Halide pipeline");
|
||||
Halide::Func targetFunc = funcsMap[targetFuncName];
|
||||
func.store_at(targetFunc, (std::string)funcNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void applyComputeAt(const FileNode& directive, Halide::Func& func,
|
||||
std::map<std::string, Halide::Func>& funcsMap)
|
||||
{
|
||||
for (const auto& funcNode : directive)
|
||||
{
|
||||
const std::string targetFuncName = funcNode.name();
|
||||
if (funcsMap.find(targetFuncName) == funcsMap.end())
|
||||
CV_Error(cv::Error::StsParseError, "Function " + targetFuncName +
|
||||
" is not represented in Halide pipeline");
|
||||
Halide::Func targetFunc = funcsMap[targetFuncName];
|
||||
func.compute_at(targetFunc, (std::string)funcNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void applyComputeRoot(const FileNode& directive, Halide::Func& func)
|
||||
{
|
||||
bool compute_root;
|
||||
directive >> compute_root;
|
||||
if (compute_root)
|
||||
func.compute_root();
|
||||
}
|
||||
|
||||
static void applyGpuBlocks(const FileNode& directive, Halide::Func& func)
|
||||
{
|
||||
std::string varName;
|
||||
for (int i = 0, n = directive.size(); i < n; ++i)
|
||||
{
|
||||
directive[i] >> varName;
|
||||
func.gpu_blocks(Halide::Var(varName));
|
||||
}
|
||||
}
|
||||
|
||||
static void applyGpuThreads(const FileNode& directive, Halide::Func& func)
|
||||
{
|
||||
std::string varName;
|
||||
for (int i = 0, n = directive.size(); i < n; ++i)
|
||||
{
|
||||
directive[i] >> varName;
|
||||
func.gpu_threads(Halide::Var(varName));
|
||||
}
|
||||
}
|
||||
|
||||
static void apply(const FileNode& directives, Halide::Func& func,
|
||||
std::map<std::string, Halide::Func>& funcsMap,
|
||||
const FileNode& params)
|
||||
{
|
||||
for (const auto& directive : directives)
|
||||
{
|
||||
if (directive.name() == "split")
|
||||
applySplit(directive, func, params);
|
||||
else if (directive.name() == "reorder")
|
||||
applyReorder(directive, func);
|
||||
else if (directive.name() == "fuse")
|
||||
applyFuse(directive, func);
|
||||
else if (directive.name() == "parallel")
|
||||
applyParallel(directive, func);
|
||||
else if (directive.name() == "unroll")
|
||||
applyUnroll(directive, func);
|
||||
else if (directive.name() == "vectorize")
|
||||
applyVectorize(directive, func, params);
|
||||
else if (directive.name() == "store_at")
|
||||
applyStoreAt(directive, func, funcsMap);
|
||||
else if (directive.name() == "compute_at")
|
||||
applyComputeAt(directive, func, funcsMap);
|
||||
else if (directive.name() == "compute_root")
|
||||
applyComputeRoot(directive, func);
|
||||
else if (directive.name() == "gpu_blocks")
|
||||
applyGpuBlocks(directive, func);
|
||||
else if (directive.name() == "gpu_threads")
|
||||
applyGpuThreads(directive, func);
|
||||
else
|
||||
CV_Error(Error::StsNotImplemented, "Scheduling directive " +
|
||||
directive.name() + " is not implemented.");
|
||||
}
|
||||
}
|
||||
|
||||
// Remove any numeric symbols after '$' sign.
|
||||
static std::string Deunique(std::string str)
|
||||
{
|
||||
int pos = -1;
|
||||
do
|
||||
{
|
||||
pos = str.find('$');
|
||||
if (pos != -1)
|
||||
{
|
||||
int len = str.find_first_not_of("0123456789", pos + 1) - pos;
|
||||
str = str.replace(pos, len, "");
|
||||
}
|
||||
}
|
||||
while (pos != -1);
|
||||
return str;
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
HalideScheduler::HalideScheduler(const std::string& configFile)
|
||||
{
|
||||
if (!configFile.empty())
|
||||
fs = FileStorage(configFile, FileStorage::READ);
|
||||
}
|
||||
|
||||
HalideScheduler::~HalideScheduler()
|
||||
{
|
||||
if (fs.isOpened())
|
||||
fs.release();
|
||||
}
|
||||
|
||||
bool HalideScheduler::process(Ptr<BackendNode>& node)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
if (!fs.isOpened())
|
||||
return false;
|
||||
|
||||
const FileNode& scheduleNode = fs["scheduling"];
|
||||
if (scheduleNode.empty())
|
||||
CV_Error(cv::Error::StsParseError, "Scheduling file should has scheduling node");
|
||||
|
||||
std::string str;
|
||||
std::map<std::string, Halide::Func> funcsMap; // Scheduled functions.
|
||||
// For every function, from top to bottom, we try to find a scheduling node.
|
||||
// Scheduling is successful (return true) if for the first function (top)
|
||||
// node is respresented.
|
||||
CV_Assert(!node.empty());
|
||||
std::vector<Halide::Func>& funcs = node.dynamicCast<HalideBackendNode>()->funcs;
|
||||
for (int i = funcs.size() - 1; i >= 0; --i)
|
||||
{
|
||||
Halide::Func& func = funcs[i];
|
||||
// For functions with the same name Halide generates unique names
|
||||
// for example func, func$1, func$2.
|
||||
// They are always formed with '$' and number.
|
||||
std::string funcName = Deunique(func.name());
|
||||
|
||||
const FileNode& funcNode = scheduleNode[funcName];
|
||||
if (!funcNode.empty())
|
||||
{
|
||||
if (!funcNode["pattern"].empty())
|
||||
{
|
||||
funcNode["pattern"] >> str;
|
||||
if (fs["patterns"][str].empty())
|
||||
CV_Error(cv::Error::StsParseError, "Scheduling pattern " + str +
|
||||
" is not defined");
|
||||
apply(fs["patterns"][str], func, funcsMap, funcNode["params"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
apply(funcNode, func, funcsMap, funcNode["params"]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (funcsMap.empty())
|
||||
return false;
|
||||
}
|
||||
funcsMap[funcName] = func;
|
||||
}
|
||||
return true;
|
||||
#endif // HAVE_HALIDE
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace dnn
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,37 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2017, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#ifndef __OPENCV_DNN_HALIDE_SCHEDULER_HPP__
|
||||
#define __OPENCV_DNN_HALIDE_SCHEDULER_HPP__
|
||||
|
||||
#include <opencv2/dnn.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class HalideScheduler
|
||||
{
|
||||
public:
|
||||
HalideScheduler(const std::string& configFile);
|
||||
|
||||
~HalideScheduler();
|
||||
|
||||
// Returns true if pipeline found in scheduling file.
|
||||
// If more than one function, returns true if the top function scheduled.
|
||||
// Other functions are optional to scheduling.
|
||||
bool process(Ptr<BackendNode>& node);
|
||||
|
||||
private:
|
||||
FileStorage fs;
|
||||
};
|
||||
|
||||
} // namespace dnn
|
||||
} // namespace cv
|
||||
|
||||
#endif // __OPENCV_DNN_HALIDE_SCHEDULER_HPP__
|
||||
@@ -0,0 +1,107 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
struct AutoInitializer
|
||||
{
|
||||
bool status;
|
||||
|
||||
AutoInitializer() : status(false)
|
||||
{
|
||||
initModule();
|
||||
}
|
||||
};
|
||||
|
||||
static AutoInitializer init;
|
||||
|
||||
void initModule()
|
||||
{
|
||||
if (init.status)
|
||||
return;
|
||||
|
||||
REG_RUNTIME_LAYER_CLASS(Slice, SliceLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Split, SplitLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Concat, ConcatLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Reshape, ReshapeLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Flatten, FlattenLayer);
|
||||
|
||||
REG_RUNTIME_LAYER_CLASS(Convolution, ConvolutionLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Deconvolution, DeconvolutionLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Pooling, PoolingLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(LRN, LRNLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(InnerProduct, InnerProductLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Softmax, SoftmaxLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(MVN, MVNLayer);
|
||||
|
||||
REG_RUNTIME_LAYER_CLASS(ReLU, ReLULayer);
|
||||
REG_RUNTIME_LAYER_CLASS(ChannelsPReLU, ChannelsPReLULayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Sigmoid, SigmoidLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(TanH, TanHLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(BNLL, BNLLLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(AbsVal, AbsLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Power, PowerLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(BatchNorm, BatchNormLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(MaxUnpool, MaxUnpoolLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Dropout, BlankLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Identity, BlankLayer);
|
||||
|
||||
REG_RUNTIME_LAYER_CLASS(Crop, CropLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Eltwise, EltwiseLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Permute, PermuteLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(PriorBox, PriorBoxLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(DetectionOutput, DetectionOutputLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(NormalizeBBox, NormalizeBBoxLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Normalize, NormalizeBBoxLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Shift, ShiftLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Padding, PaddingLayer);
|
||||
REG_RUNTIME_LAYER_CLASS(Scale, ScaleLayer);
|
||||
|
||||
init.status = true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,198 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2016, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
/*
|
||||
Implementation of Batch Normalization layer.
|
||||
*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "op_halide.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class BatchNormLayerImpl : public BatchNormLayer
|
||||
{
|
||||
public:
|
||||
Mat weights_, bias_;
|
||||
|
||||
BatchNormLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
CV_Assert(blobs.size() >= 3);
|
||||
|
||||
hasWeights = params.get<bool>("has_weight", false);
|
||||
hasBias = params.get<bool>("has_bias", false);
|
||||
epsilon = params.get<float>("eps", 1E-5);
|
||||
|
||||
size_t n = blobs[0].total();
|
||||
CV_Assert(blobs[1].total() == n &&
|
||||
blobs[0].isContinuous() && blobs[1].isContinuous() &&
|
||||
blobs[0].type() == CV_32F && blobs[1].type() == CV_32F);
|
||||
|
||||
float varMeanScale = 1.f;
|
||||
if (!hasWeights && !hasBias) {
|
||||
CV_Assert(blobs[2].type() == CV_32F);
|
||||
varMeanScale = blobs[2].at<float>(0);
|
||||
if (varMeanScale != 0)
|
||||
varMeanScale = 1/varMeanScale;
|
||||
}
|
||||
|
||||
const int weightsBlobIndex = 2;
|
||||
const int biasBlobIndex = weightsBlobIndex + hasWeights;
|
||||
|
||||
if( hasWeights )
|
||||
{
|
||||
CV_Assert((size_t)weightsBlobIndex < blobs.size());
|
||||
const Mat& w = blobs[weightsBlobIndex];
|
||||
CV_Assert(w.isContinuous() && w.type() == CV_32F && w.total() == (size_t)n);
|
||||
}
|
||||
|
||||
if( hasBias )
|
||||
{
|
||||
CV_Assert((size_t)biasBlobIndex < blobs.size());
|
||||
const Mat& b = blobs[weightsBlobIndex];
|
||||
CV_Assert(b.isContinuous() && b.type() == CV_32F && b.total() == (size_t)n);
|
||||
}
|
||||
|
||||
const float* meanData = blobs[0].ptr<float>();
|
||||
const float* stdData = blobs[1].ptr<float>();
|
||||
const float* weightsData = hasWeights ? blobs[weightsBlobIndex].ptr<float>() : 0;
|
||||
const float* biasData = hasBias ? blobs[biasBlobIndex].ptr<float>() : 0;
|
||||
|
||||
weights_.create(1, (int)n, CV_32F);
|
||||
bias_.create(1, (int)n, CV_32F);
|
||||
|
||||
float* dstWeightsData = weights_.ptr<float>();
|
||||
float* dstBiasData = bias_.ptr<float>();
|
||||
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
float w = (hasWeights ? weightsData[i] : 1.0f) / sqrt(stdData[i] * varMeanScale + epsilon);
|
||||
dstWeightsData[i] = w;
|
||||
dstBiasData[i] = (hasBias ? biasData[i] : 0.0f) - w * meanData[i] * varMeanScale;
|
||||
}
|
||||
}
|
||||
|
||||
void getScaleShift(Mat& scale, Mat& shift) const
|
||||
{
|
||||
scale = weights_;
|
||||
shift = bias_;
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide();
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
CV_Assert(blobs.size() >= 2);
|
||||
CV_Assert(inputs.size() == 1);
|
||||
|
||||
Mat &inpBlob = *inputs[0];
|
||||
int rows = inpBlob.size[2];
|
||||
int cols = inpBlob.size[3];
|
||||
|
||||
for (size_t ii = 0; ii < outputs.size(); ii++)
|
||||
{
|
||||
Mat &outBlob = outputs[ii];
|
||||
|
||||
for(int num = 0; num < outBlob.size[0]; num++)
|
||||
{
|
||||
for (int n = 0; n < outBlob.size[1]; n++)
|
||||
{
|
||||
float w = weights_.at<float>(n);
|
||||
float b = bias_.at<float>(n);
|
||||
Mat inpBlobPlane(rows, cols, CV_32F, inpBlob.ptr<float>(num, n));
|
||||
Mat outBlobPlane(rows, cols, CV_32F, outBlob.ptr<float>(num, n));
|
||||
inpBlobPlane.convertTo(outBlobPlane, CV_32F, w, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> tryAttach(const Ptr<BackendNode>& node)
|
||||
{
|
||||
switch (node->backendId)
|
||||
{
|
||||
case DNN_BACKEND_HALIDE:
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
auto base = node.dynamicCast<HalideBackendNode>();
|
||||
Halide::Func& input = base->funcs.back();
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = attachHalide(input(x, y, c, n));
|
||||
return Ptr<BackendNode>(new HalideBackendNode(base, top));
|
||||
#endif // HAVE_HALIDE
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
Halide::Buffer<float> input = halideBuffer(inputs[0]);
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = attachHalide(input(x, y, c, n));
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
// attachHalide can work both with Halide::Buffer and Halide::Func. In the
|
||||
// second case it will be a fusion.
|
||||
Halide::Func attachHalide(const Halide::Expr& input)
|
||||
{
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
|
||||
const int numChannels = weights_.total();
|
||||
auto weights = wrapToHalideBuffer(weights_, {numChannels});
|
||||
auto bias = wrapToHalideBuffer(bias_, {numChannels});
|
||||
top(x, y, c, n) = input * weights(c) + bias(c);
|
||||
return top;
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)outputs; // suppress unused variable warning
|
||||
|
||||
int64 flops = 0;
|
||||
for(int i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
flops += 3*total(inputs[i]);
|
||||
}
|
||||
return flops;
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<BatchNormLayer> BatchNormLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<BatchNormLayer>(new BatchNormLayerImpl(params));
|
||||
}
|
||||
|
||||
} // namespace dnn
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,78 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
#include "../precomp.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
class BlankLayerImpl : public BlankLayer
|
||||
{
|
||||
public:
|
||||
BlankLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
|
||||
return true;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
for (int i = 0, n = outputs.size(); i < n; ++i)
|
||||
if (outputs[i].data != inputs[i]->data)
|
||||
inputs[i]->copyTo(outputs[i]);
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<BlankLayer> BlankLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<BlankLayer>(new BlankLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "op_halide.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class ConcatLayerImpl : public ConcatLayer
|
||||
{
|
||||
public:
|
||||
ConcatLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
axis = params.get<int>("axis", 1);
|
||||
}
|
||||
|
||||
virtual bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() > 0);
|
||||
outputs.clear();
|
||||
outputs.push_back(inputs[0]);
|
||||
int cAxis = clamp(axis, inputs[0]);
|
||||
|
||||
int axisSum = 0;
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
MatShape curShape = inputs[i];
|
||||
|
||||
CV_Assert(curShape.size() == outputs.back().size());
|
||||
for (int curAxis = 0; curAxis < outputs.back().size(); curAxis++)
|
||||
{
|
||||
if (curAxis != cAxis && outputs.back()[curAxis] != curShape[curAxis])
|
||||
CV_Error(Error::StsBadSize, "Inconsitent shape for ConcatLayer");
|
||||
}
|
||||
|
||||
axisSum += curShape[cAxis];
|
||||
}
|
||||
|
||||
outputs.back()[cAxis] = axisSum;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide() && axis == 1; // By channels
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
int cAxis = clamp(axis, inputs[0]->dims);
|
||||
Mat& outMat = outputs[0];
|
||||
std::vector<Range> ranges(outputs[0].dims, Range::all());
|
||||
|
||||
ranges[cAxis].start = 0;
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
ranges[cAxis].end = ranges[cAxis].start + inputs[i]->size[cAxis];
|
||||
inputs[i]->copyTo(outMat(&ranges[0]));
|
||||
ranges[cAxis].start = ranges[cAxis].end;
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &input)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
std::vector<Halide::Buffer<> > inputBuffers = halideBuffers(input);
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
int offset = inputBuffers[0].channels();
|
||||
Halide::Expr topExpr = select(c < offset,
|
||||
inputBuffers[0](x, y, c, n),
|
||||
inputBuffers[1](x, y, c - offset, n));
|
||||
for (int i = 2; i < input.size(); ++i)
|
||||
{
|
||||
offset += inputBuffers[i - 1].channels();
|
||||
topExpr = select(c < offset, topExpr,
|
||||
inputBuffers[i](x, y, c - offset, n));
|
||||
}
|
||||
top(x, y, c, n) = topExpr;
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<ConcatLayer> ConcatLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<ConcatLayer>(new ConcatLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,153 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class CropLayerImpl : public CropLayer
|
||||
{
|
||||
public:
|
||||
CropLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
startAxis = params.get<int>("axis", 2);
|
||||
const DictValue *paramOffset = params.ptr("offset");
|
||||
|
||||
if (paramOffset)
|
||||
{
|
||||
for (int i = 0; i < paramOffset->size(); i++)
|
||||
offset.push_back(paramOffset->get<int>(i));
|
||||
}
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() == 2);
|
||||
|
||||
MatShape dstShape = inputs[0];
|
||||
int start = clamp(startAxis, dstShape);
|
||||
for (int i = start; i < dstShape.size(); i++)
|
||||
{
|
||||
dstShape[i] = inputs[1][i];
|
||||
}
|
||||
|
||||
outputs.resize(1, dstShape);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void finalize(const std::vector<Mat *> &inputs, std::vector<Mat> &outputs)
|
||||
{
|
||||
CV_Assert(2 == inputs.size());
|
||||
|
||||
const Mat &inpBlob = *inputs[0];
|
||||
const Mat &inpSzBlob = *inputs[1];
|
||||
|
||||
int dims = inpBlob.dims;
|
||||
int start_axis = clamp(startAxis, dims);
|
||||
|
||||
std::vector<int> offset_final(dims, 0);
|
||||
if (offset.size() == 1)
|
||||
{
|
||||
for (int i = start_axis; i < dims; i++)
|
||||
offset_final[i] = offset[0];
|
||||
}
|
||||
else if (offset.size() > 1)
|
||||
{
|
||||
if ((int)offset.size() != dims - start_axis)
|
||||
CV_Error(Error::StsBadArg, "number of offset values specified must be "
|
||||
"equal to the number of dimensions following axis.");
|
||||
|
||||
for (int i = start_axis; i < dims; i++)
|
||||
offset_final[i] = offset[i - start_axis];
|
||||
}
|
||||
|
||||
crop_ranges.resize(dims, Range::all());
|
||||
for (int i = 0; i < dims; i++)
|
||||
{
|
||||
if( i < start_axis )
|
||||
continue;
|
||||
|
||||
if (!offset.empty()) //normal case
|
||||
{
|
||||
if (offset_final[i] < 0 || offset_final[i] + inpSzBlob.size[i] > inpBlob.size[i])
|
||||
CV_Error(Error::StsBadArg, "invalid crop parameters");
|
||||
|
||||
crop_ranges[i] = Range(offset_final[i], offset_final[i] + inpSzBlob.size[i]);
|
||||
}
|
||||
else //detect offset automatically so that cropped image is center of original one
|
||||
{
|
||||
if (inpSzBlob.size[i] > inpBlob.size[i])
|
||||
CV_Error(Error::StsBadArg, "invalid output blob size");
|
||||
|
||||
int cur_crop = (inpBlob.size[i] - inpSzBlob.size[i]) / 2;
|
||||
crop_ranges[i] = Range(cur_crop, cur_crop + inpSzBlob.size[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat *> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
Mat &input = *inputs[0];
|
||||
Mat &output = outputs[0];
|
||||
|
||||
input(&crop_ranges[0]).copyTo(output);
|
||||
}
|
||||
|
||||
std::vector<Range> crop_ranges;
|
||||
};
|
||||
|
||||
|
||||
Ptr<CropLayer> CropLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<CropLayer>(new CropLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,798 @@
|
||||
/*M ///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include <float.h>
|
||||
#include <string>
|
||||
#include <caffe.pb.h>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
namespace util
|
||||
{
|
||||
|
||||
template <typename T>
|
||||
std::string to_string(T value)
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << value;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void make_error(const std::string& message1, const T& message2)
|
||||
{
|
||||
std::string error(message1);
|
||||
error += std::string(util::to_string<int>(message2));
|
||||
CV_Error(Error::StsBadArg, error.c_str());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool SortScorePairDescend(const std::pair<float, T>& pair1,
|
||||
const std::pair<float, T>& pair2)
|
||||
{
|
||||
return pair1.first > pair2.first;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class DetectionOutputLayerImpl : public DetectionOutputLayer
|
||||
{
|
||||
public:
|
||||
unsigned _numClasses;
|
||||
bool _shareLocation;
|
||||
int _numLocClasses;
|
||||
|
||||
int _backgroundLabelId;
|
||||
|
||||
typedef caffe::PriorBoxParameter_CodeType CodeType;
|
||||
CodeType _codeType;
|
||||
|
||||
bool _varianceEncodedInTarget;
|
||||
int _keepTopK;
|
||||
float _confidenceThreshold;
|
||||
|
||||
float _nmsThreshold;
|
||||
int _topK;
|
||||
|
||||
enum { _numAxes = 4 };
|
||||
static const std::string _layerName;
|
||||
|
||||
typedef std::map<int, std::vector<caffe::NormalizedBBox> > LabelBBox;
|
||||
|
||||
bool getParameterDict(const LayerParams ¶ms,
|
||||
const std::string ¶meterName,
|
||||
DictValue& result)
|
||||
{
|
||||
if (!params.has(parameterName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = params.get(parameterName);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T getParameter(const LayerParams ¶ms,
|
||||
const std::string ¶meterName,
|
||||
const size_t &idx=0,
|
||||
const bool required=true,
|
||||
const T& defaultValue=T())
|
||||
{
|
||||
DictValue dictValue;
|
||||
bool success = getParameterDict(params, parameterName, dictValue);
|
||||
if(!success)
|
||||
{
|
||||
if(required)
|
||||
{
|
||||
std::string message = _layerName;
|
||||
message += " layer parameter does not contain ";
|
||||
message += parameterName;
|
||||
message += " parameter.";
|
||||
CV_Error(Error::StsBadArg, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return dictValue.get<T>(idx);
|
||||
}
|
||||
|
||||
void getCodeType(const LayerParams ¶ms)
|
||||
{
|
||||
String codeTypeString = params.get<String>("code_type").toLowerCase();
|
||||
if (codeTypeString == "corner")
|
||||
_codeType = caffe::PriorBoxParameter_CodeType_CORNER;
|
||||
else if (codeTypeString == "center_size")
|
||||
_codeType = caffe::PriorBoxParameter_CodeType_CENTER_SIZE;
|
||||
else
|
||||
_codeType = caffe::PriorBoxParameter_CodeType_CORNER;
|
||||
}
|
||||
|
||||
DetectionOutputLayerImpl(const LayerParams ¶ms)
|
||||
{
|
||||
_numClasses = getParameter<unsigned>(params, "num_classes");
|
||||
_shareLocation = getParameter<bool>(params, "share_location");
|
||||
_numLocClasses = _shareLocation ? 1 : _numClasses;
|
||||
_backgroundLabelId = getParameter<int>(params, "background_label_id");
|
||||
_varianceEncodedInTarget = getParameter<bool>(params, "variance_encoded_in_target", 0, false, false);
|
||||
_keepTopK = getParameter<int>(params, "keep_top_k");
|
||||
_confidenceThreshold = getParameter<float>(params, "confidence_threshold", 0, false, -FLT_MAX);
|
||||
_topK = getParameter<int>(params, "top_k", 0, false, -1);
|
||||
|
||||
getCodeType(params);
|
||||
|
||||
// Parameters used in nms.
|
||||
_nmsThreshold = getParameter<float>(params, "nms_threshold");
|
||||
CV_Assert(_nmsThreshold > 0.);
|
||||
|
||||
setParamsFrom(params);
|
||||
}
|
||||
|
||||
void checkInputs(const std::vector<Mat*> &inputs)
|
||||
{
|
||||
for (size_t i = 1; i < inputs.size(); i++)
|
||||
{
|
||||
CV_Assert(inputs[i]->size == inputs[0]->size);
|
||||
}
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() > 0);
|
||||
CV_Assert(inputs[0][0] == inputs[1][0]);
|
||||
|
||||
int numPriors = inputs[2][2] / 4;
|
||||
CV_Assert((numPriors * _numLocClasses * 4) == inputs[0][1]);
|
||||
CV_Assert(int(numPriors * _numClasses) == inputs[1][1]);
|
||||
|
||||
// num() and channels() are 1.
|
||||
// Since the number of bboxes to be kept is unknown before nms, we manually
|
||||
// set it to (fake) 1.
|
||||
// Each row is a 7 dimension std::vector, which stores
|
||||
// [image_id, label, confidence, xmin, ymin, xmax, ymax]
|
||||
outputs.resize(1, shape(1, 1, 1, 7));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
const float* locationData = inputs[0]->ptr<float>();
|
||||
const float* confidenceData = inputs[1]->ptr<float>();
|
||||
const float* priorData = inputs[2]->ptr<float>();
|
||||
|
||||
int num = inputs[0]->size[0];
|
||||
int numPriors = inputs[2]->size[2] / 4;
|
||||
|
||||
// Retrieve all location predictions.
|
||||
std::vector<LabelBBox> allLocationPredictions;
|
||||
GetLocPredictions(locationData, num, numPriors, _numLocClasses,
|
||||
_shareLocation, &allLocationPredictions);
|
||||
|
||||
// Retrieve all confidences.
|
||||
std::vector<std::map<int, std::vector<float> > > allConfidenceScores;
|
||||
GetConfidenceScores(confidenceData, num, numPriors, _numClasses,
|
||||
&allConfidenceScores);
|
||||
|
||||
// Retrieve all prior bboxes. It is same within a batch since we assume all
|
||||
// images in a batch are of same dimension.
|
||||
std::vector<caffe::NormalizedBBox> priorBBoxes;
|
||||
std::vector<std::vector<float> > priorVariances;
|
||||
GetPriorBBoxes(priorData, numPriors, &priorBBoxes, &priorVariances);
|
||||
|
||||
const bool clip_bbox = false;
|
||||
// Decode all loc predictions to bboxes.
|
||||
std::vector<LabelBBox> allDecodedBBoxes;
|
||||
DecodeBBoxesAll(allLocationPredictions, priorBBoxes, priorVariances, num,
|
||||
_shareLocation, _numLocClasses, _backgroundLabelId,
|
||||
_codeType, _varianceEncodedInTarget, clip_bbox, &allDecodedBBoxes);
|
||||
|
||||
int numKept = 0;
|
||||
std::vector<std::map<int, std::vector<int> > > allIndices;
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
const LabelBBox& decodeBBoxes = allDecodedBBoxes[i];
|
||||
const std::map<int, std::vector<float> >& confidenceScores =
|
||||
allConfidenceScores[i];
|
||||
std::map<int, std::vector<int> > indices;
|
||||
int numDetections = 0;
|
||||
for (int c = 0; c < (int)_numClasses; ++c)
|
||||
{
|
||||
if (c == _backgroundLabelId)
|
||||
{
|
||||
// Ignore background class.
|
||||
continue;
|
||||
}
|
||||
if (confidenceScores.find(c) == confidenceScores.end())
|
||||
{
|
||||
// Something bad happened if there are no predictions for current label.
|
||||
util::make_error<int>("Could not find confidence predictions for label ", c);
|
||||
}
|
||||
|
||||
const std::vector<float>& scores = confidenceScores.find(c)->second;
|
||||
int label = _shareLocation ? -1 : c;
|
||||
if (decodeBBoxes.find(label) == decodeBBoxes.end())
|
||||
{
|
||||
// Something bad happened if there are no predictions for current label.
|
||||
util::make_error<int>("Could not find location predictions for label ", label);
|
||||
continue;
|
||||
}
|
||||
const std::vector<caffe::NormalizedBBox>& bboxes =
|
||||
decodeBBoxes.find(label)->second;
|
||||
ApplyNMSFast(bboxes, scores, _confidenceThreshold, _nmsThreshold, 1.0,
|
||||
_topK, &(indices[c]));
|
||||
numDetections += indices[c].size();
|
||||
}
|
||||
if (_keepTopK > -1 && numDetections > _keepTopK)
|
||||
{
|
||||
std::vector<std::pair<float, std::pair<int, int> > > scoreIndexPairs;
|
||||
for (std::map<int, std::vector<int> >::iterator it = indices.begin();
|
||||
it != indices.end(); ++it)
|
||||
{
|
||||
int label = it->first;
|
||||
const std::vector<int>& labelIndices = it->second;
|
||||
if (confidenceScores.find(label) == confidenceScores.end())
|
||||
{
|
||||
// Something bad happened for current label.
|
||||
util::make_error<int>("Could not find location predictions for label ", label);
|
||||
continue;
|
||||
}
|
||||
const std::vector<float>& scores = confidenceScores.find(label)->second;
|
||||
for (size_t j = 0; j < labelIndices.size(); ++j)
|
||||
{
|
||||
size_t idx = labelIndices[j];
|
||||
CV_Assert(idx < scores.size());
|
||||
scoreIndexPairs.push_back(
|
||||
std::make_pair(scores[idx], std::make_pair(label, idx)));
|
||||
}
|
||||
}
|
||||
// Keep outputs k results per image.
|
||||
std::sort(scoreIndexPairs.begin(), scoreIndexPairs.end(),
|
||||
util::SortScorePairDescend<std::pair<int, int> >);
|
||||
scoreIndexPairs.resize(_keepTopK);
|
||||
// Store the new indices.
|
||||
std::map<int, std::vector<int> > newIndices;
|
||||
for (size_t j = 0; j < scoreIndexPairs.size(); ++j)
|
||||
{
|
||||
int label = scoreIndexPairs[j].second.first;
|
||||
int idx = scoreIndexPairs[j].second.second;
|
||||
newIndices[label].push_back(idx);
|
||||
}
|
||||
allIndices.push_back(newIndices);
|
||||
numKept += _keepTopK;
|
||||
}
|
||||
else
|
||||
{
|
||||
allIndices.push_back(indices);
|
||||
numKept += numDetections;
|
||||
}
|
||||
}
|
||||
|
||||
if (numKept == 0)
|
||||
{
|
||||
CV_ErrorNoReturn(Error::StsError, "Couldn't find any detections");
|
||||
return;
|
||||
}
|
||||
int outputShape[] = {1, 1, numKept, 7};
|
||||
outputs[0].create(4, outputShape, CV_32F);
|
||||
float* outputsData = outputs[0].ptr<float>();
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
const std::map<int, std::vector<float> >& confidenceScores =
|
||||
allConfidenceScores[i];
|
||||
const LabelBBox& decodeBBoxes = allDecodedBBoxes[i];
|
||||
for (std::map<int, std::vector<int> >::iterator it = allIndices[i].begin();
|
||||
it != allIndices[i].end(); ++it)
|
||||
{
|
||||
int label = it->first;
|
||||
if (confidenceScores.find(label) == confidenceScores.end())
|
||||
{
|
||||
// Something bad happened if there are no predictions for current label.
|
||||
util::make_error<int>("Could not find confidence predictions for label ", label);
|
||||
continue;
|
||||
}
|
||||
const std::vector<float>& scores = confidenceScores.find(label)->second;
|
||||
int locLabel = _shareLocation ? -1 : label;
|
||||
if (decodeBBoxes.find(locLabel) == decodeBBoxes.end())
|
||||
{
|
||||
// Something bad happened if there are no predictions for current label.
|
||||
util::make_error<int>("Could not find location predictions for label ", locLabel);
|
||||
continue;
|
||||
}
|
||||
const std::vector<caffe::NormalizedBBox>& bboxes =
|
||||
decodeBBoxes.find(locLabel)->second;
|
||||
std::vector<int>& indices = it->second;
|
||||
|
||||
for (size_t j = 0; j < indices.size(); ++j)
|
||||
{
|
||||
int idx = indices[j];
|
||||
outputsData[count * 7] = i;
|
||||
outputsData[count * 7 + 1] = label;
|
||||
outputsData[count * 7 + 2] = scores[idx];
|
||||
caffe::NormalizedBBox clipBBox = bboxes[idx];
|
||||
outputsData[count * 7 + 3] = clipBBox.xmin();
|
||||
outputsData[count * 7 + 4] = clipBBox.ymin();
|
||||
outputsData[count * 7 + 5] = clipBBox.xmax();
|
||||
outputsData[count * 7 + 6] = clipBBox.ymax();
|
||||
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute bbox size.
|
||||
float BBoxSize(const caffe::NormalizedBBox& bbox,
|
||||
const bool normalized=true)
|
||||
{
|
||||
if (bbox.xmax() < bbox.xmin() || bbox.ymax() < bbox.ymin())
|
||||
{
|
||||
// If bbox is invalid (e.g. xmax < xmin or ymax < ymin), return 0.
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bbox.has_size())
|
||||
{
|
||||
return bbox.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
float width = bbox.xmax() - bbox.xmin();
|
||||
float height = bbox.ymax() - bbox.ymin();
|
||||
if (normalized)
|
||||
{
|
||||
return width * height;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If bbox is not within range [0, 1].
|
||||
return (width + 1) * (height + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clip the caffe::NormalizedBBox such that the range for each corner is [0, 1].
|
||||
void ClipBBox(const caffe::NormalizedBBox& bbox,
|
||||
caffe::NormalizedBBox* clipBBox)
|
||||
{
|
||||
clipBBox->set_xmin(std::max(std::min(bbox.xmin(), 1.f), 0.f));
|
||||
clipBBox->set_ymin(std::max(std::min(bbox.ymin(), 1.f), 0.f));
|
||||
clipBBox->set_xmax(std::max(std::min(bbox.xmax(), 1.f), 0.f));
|
||||
clipBBox->set_ymax(std::max(std::min(bbox.ymax(), 1.f), 0.f));
|
||||
clipBBox->clear_size();
|
||||
clipBBox->set_size(BBoxSize(*clipBBox));
|
||||
clipBBox->set_difficult(bbox.difficult());
|
||||
}
|
||||
|
||||
// Decode a bbox according to a prior bbox.
|
||||
void DecodeBBox(
|
||||
const caffe::NormalizedBBox& prior_bbox, const std::vector<float>& prior_variance,
|
||||
const CodeType code_type, const bool variance_encoded_in_target,
|
||||
const bool clip_bbox, const caffe::NormalizedBBox& bbox,
|
||||
caffe::NormalizedBBox* decode_bbox) {
|
||||
if (code_type == caffe::PriorBoxParameter_CodeType_CORNER) {
|
||||
if (variance_encoded_in_target) {
|
||||
// variance is encoded in target, we simply need to add the offset
|
||||
// predictions.
|
||||
decode_bbox->set_xmin(prior_bbox.xmin() + bbox.xmin());
|
||||
decode_bbox->set_ymin(prior_bbox.ymin() + bbox.ymin());
|
||||
decode_bbox->set_xmax(prior_bbox.xmax() + bbox.xmax());
|
||||
decode_bbox->set_ymax(prior_bbox.ymax() + bbox.ymax());
|
||||
} else {
|
||||
// variance is encoded in bbox, we need to scale the offset accordingly.
|
||||
decode_bbox->set_xmin(
|
||||
prior_bbox.xmin() + prior_variance[0] * bbox.xmin());
|
||||
decode_bbox->set_ymin(
|
||||
prior_bbox.ymin() + prior_variance[1] * bbox.ymin());
|
||||
decode_bbox->set_xmax(
|
||||
prior_bbox.xmax() + prior_variance[2] * bbox.xmax());
|
||||
decode_bbox->set_ymax(
|
||||
prior_bbox.ymax() + prior_variance[3] * bbox.ymax());
|
||||
}
|
||||
} else if (code_type == caffe::PriorBoxParameter_CodeType_CENTER_SIZE) {
|
||||
float prior_width = prior_bbox.xmax() - prior_bbox.xmin();
|
||||
CV_Assert(prior_width > 0);
|
||||
float prior_height = prior_bbox.ymax() - prior_bbox.ymin();
|
||||
CV_Assert(prior_height > 0);
|
||||
float prior_center_x = (prior_bbox.xmin() + prior_bbox.xmax()) / 2.;
|
||||
float prior_center_y = (prior_bbox.ymin() + prior_bbox.ymax()) / 2.;
|
||||
|
||||
float decode_bbox_center_x, decode_bbox_center_y;
|
||||
float decode_bbox_width, decode_bbox_height;
|
||||
if (variance_encoded_in_target) {
|
||||
// variance is encoded in target, we simply need to retore the offset
|
||||
// predictions.
|
||||
decode_bbox_center_x = bbox.xmin() * prior_width + prior_center_x;
|
||||
decode_bbox_center_y = bbox.ymin() * prior_height + prior_center_y;
|
||||
decode_bbox_width = exp(bbox.xmax()) * prior_width;
|
||||
decode_bbox_height = exp(bbox.ymax()) * prior_height;
|
||||
} else {
|
||||
// variance is encoded in bbox, we need to scale the offset accordingly.
|
||||
decode_bbox_center_x =
|
||||
prior_variance[0] * bbox.xmin() * prior_width + prior_center_x;
|
||||
decode_bbox_center_y =
|
||||
prior_variance[1] * bbox.ymin() * prior_height + prior_center_y;
|
||||
decode_bbox_width =
|
||||
exp(prior_variance[2] * bbox.xmax()) * prior_width;
|
||||
decode_bbox_height =
|
||||
exp(prior_variance[3] * bbox.ymax()) * prior_height;
|
||||
}
|
||||
|
||||
decode_bbox->set_xmin(decode_bbox_center_x - decode_bbox_width / 2.);
|
||||
decode_bbox->set_ymin(decode_bbox_center_y - decode_bbox_height / 2.);
|
||||
decode_bbox->set_xmax(decode_bbox_center_x + decode_bbox_width / 2.);
|
||||
decode_bbox->set_ymax(decode_bbox_center_y + decode_bbox_height / 2.);
|
||||
} else {
|
||||
CV_Error(Error::StsBadArg, "Unknown LocLossType.");
|
||||
}
|
||||
float bbox_size = BBoxSize(*decode_bbox);
|
||||
decode_bbox->set_size(bbox_size);
|
||||
if (clip_bbox) {
|
||||
ClipBBox(*decode_bbox, decode_bbox);
|
||||
}
|
||||
}
|
||||
|
||||
// Decode a set of bboxes according to a set of prior bboxes.
|
||||
void DecodeBBoxes(
|
||||
const std::vector<caffe::NormalizedBBox>& prior_bboxes,
|
||||
const std::vector<std::vector<float> >& prior_variances,
|
||||
const CodeType code_type, const bool variance_encoded_in_target,
|
||||
const bool clip_bbox, const std::vector<caffe::NormalizedBBox>& bboxes,
|
||||
std::vector<caffe::NormalizedBBox>* decode_bboxes) {
|
||||
CV_Assert(prior_bboxes.size() == prior_variances.size());
|
||||
CV_Assert(prior_bboxes.size() == bboxes.size());
|
||||
int num_bboxes = prior_bboxes.size();
|
||||
if (num_bboxes >= 1) {
|
||||
CV_Assert(prior_variances[0].size() == 4);
|
||||
}
|
||||
decode_bboxes->clear();
|
||||
for (int i = 0; i < num_bboxes; ++i) {
|
||||
caffe::NormalizedBBox decode_bbox;
|
||||
DecodeBBox(prior_bboxes[i], prior_variances[i], code_type,
|
||||
variance_encoded_in_target, clip_bbox, bboxes[i], &decode_bbox);
|
||||
decode_bboxes->push_back(decode_bbox);
|
||||
}
|
||||
}
|
||||
|
||||
// Decode all bboxes in a batch.
|
||||
void DecodeBBoxesAll(const std::vector<LabelBBox>& all_loc_preds,
|
||||
const std::vector<caffe::NormalizedBBox>& prior_bboxes,
|
||||
const std::vector<std::vector<float> >& prior_variances,
|
||||
const int num, const bool share_location,
|
||||
const int num_loc_classes, const int background_label_id,
|
||||
const CodeType code_type, const bool variance_encoded_in_target,
|
||||
const bool clip, std::vector<LabelBBox>* all_decode_bboxes) {
|
||||
CV_Assert(all_loc_preds.size() == num);
|
||||
all_decode_bboxes->clear();
|
||||
all_decode_bboxes->resize(num);
|
||||
for (int i = 0; i < num; ++i) {
|
||||
// Decode predictions into bboxes.
|
||||
LabelBBox& decode_bboxes = (*all_decode_bboxes)[i];
|
||||
for (int c = 0; c < num_loc_classes; ++c) {
|
||||
int label = share_location ? -1 : c;
|
||||
if (label == background_label_id) {
|
||||
// Ignore background class.
|
||||
continue;
|
||||
}
|
||||
if (all_loc_preds[i].find(label) == all_loc_preds[i].end()) {
|
||||
// Something bad happened if there are no predictions for current label.
|
||||
util::make_error<int>("Could not find location predictions for label ", label);
|
||||
}
|
||||
const std::vector<caffe::NormalizedBBox>& label_loc_preds =
|
||||
all_loc_preds[i].find(label)->second;
|
||||
DecodeBBoxes(prior_bboxes, prior_variances,
|
||||
code_type, variance_encoded_in_target, clip,
|
||||
label_loc_preds, &(decode_bboxes[label]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get prior bounding boxes from prior_data.
|
||||
// prior_data: 1 x 2 x num_priors * 4 x 1 blob.
|
||||
// num_priors: number of priors.
|
||||
// prior_bboxes: stores all the prior bboxes in the format of caffe::NormalizedBBox.
|
||||
// prior_variances: stores all the variances needed by prior bboxes.
|
||||
void GetPriorBBoxes(const float* priorData, const int& numPriors,
|
||||
std::vector<caffe::NormalizedBBox>* priorBBoxes,
|
||||
std::vector<std::vector<float> >* priorVariances)
|
||||
{
|
||||
priorBBoxes->clear();
|
||||
priorVariances->clear();
|
||||
for (int i = 0; i < numPriors; ++i)
|
||||
{
|
||||
int startIdx = i * 4;
|
||||
caffe::NormalizedBBox bbox;
|
||||
bbox.set_xmin(priorData[startIdx]);
|
||||
bbox.set_ymin(priorData[startIdx + 1]);
|
||||
bbox.set_xmax(priorData[startIdx + 2]);
|
||||
bbox.set_ymax(priorData[startIdx + 3]);
|
||||
float bboxSize = BBoxSize(bbox);
|
||||
bbox.set_size(bboxSize);
|
||||
priorBBoxes->push_back(bbox);
|
||||
}
|
||||
|
||||
for (int i = 0; i < numPriors; ++i)
|
||||
{
|
||||
int startIdx = (numPriors + i) * 4;
|
||||
std::vector<float> var;
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
var.push_back(priorData[startIdx + j]);
|
||||
}
|
||||
priorVariances->push_back(var);
|
||||
}
|
||||
}
|
||||
|
||||
// Scale the caffe::NormalizedBBox w.r.t. height and width.
|
||||
void ScaleBBox(const caffe::NormalizedBBox& bbox,
|
||||
const int height, const int width,
|
||||
caffe::NormalizedBBox* scaleBBox)
|
||||
{
|
||||
scaleBBox->set_xmin(bbox.xmin() * width);
|
||||
scaleBBox->set_ymin(bbox.ymin() * height);
|
||||
scaleBBox->set_xmax(bbox.xmax() * width);
|
||||
scaleBBox->set_ymax(bbox.ymax() * height);
|
||||
scaleBBox->clear_size();
|
||||
bool normalized = !(width > 1 || height > 1);
|
||||
scaleBBox->set_size(BBoxSize(*scaleBBox, normalized));
|
||||
scaleBBox->set_difficult(bbox.difficult());
|
||||
}
|
||||
|
||||
// Get location predictions from loc_data.
|
||||
// loc_data: num x num_preds_per_class * num_loc_classes * 4 blob.
|
||||
// num: the number of images.
|
||||
// num_preds_per_class: number of predictions per class.
|
||||
// num_loc_classes: number of location classes. It is 1 if share_location is
|
||||
// true; and is equal to number of classes needed to predict otherwise.
|
||||
// share_location: if true, all classes share the same location prediction.
|
||||
// loc_preds: stores the location prediction, where each item contains
|
||||
// location prediction for an image.
|
||||
void GetLocPredictions(const float* locData, const int num,
|
||||
const int numPredsPerClass, const int numLocClasses,
|
||||
const bool shareLocation, std::vector<LabelBBox>* locPreds)
|
||||
{
|
||||
locPreds->clear();
|
||||
if (shareLocation)
|
||||
{
|
||||
CV_Assert(numLocClasses == 1);
|
||||
}
|
||||
locPreds->resize(num);
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
LabelBBox& labelBBox = (*locPreds)[i];
|
||||
for (int p = 0; p < numPredsPerClass; ++p)
|
||||
{
|
||||
int startIdx = p * numLocClasses * 4;
|
||||
for (int c = 0; c < numLocClasses; ++c)
|
||||
{
|
||||
int label = shareLocation ? -1 : c;
|
||||
if (labelBBox.find(label) == labelBBox.end())
|
||||
{
|
||||
labelBBox[label].resize(numPredsPerClass);
|
||||
}
|
||||
labelBBox[label][p].set_xmin(locData[startIdx + c * 4]);
|
||||
labelBBox[label][p].set_ymin(locData[startIdx + c * 4 + 1]);
|
||||
labelBBox[label][p].set_xmax(locData[startIdx + c * 4 + 2]);
|
||||
labelBBox[label][p].set_ymax(locData[startIdx + c * 4 + 3]);
|
||||
}
|
||||
}
|
||||
locData += numPredsPerClass * numLocClasses * 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Get confidence predictions from conf_data.
|
||||
// conf_data: num x num_preds_per_class * num_classes blob.
|
||||
// num: the number of images.
|
||||
// num_preds_per_class: number of predictions per class.
|
||||
// num_classes: number of classes.
|
||||
// conf_preds: stores the confidence prediction, where each item contains
|
||||
// confidence prediction for an image.
|
||||
void GetConfidenceScores(const float* confData, const int num,
|
||||
const int numPredsPerClass, const int numClasses,
|
||||
std::vector<std::map<int, std::vector<float> > >* confPreds)
|
||||
{
|
||||
confPreds->clear();
|
||||
confPreds->resize(num);
|
||||
for (int i = 0; i < num; ++i)
|
||||
{
|
||||
std::map<int, std::vector<float> >& labelScores = (*confPreds)[i];
|
||||
for (int p = 0; p < numPredsPerClass; ++p)
|
||||
{
|
||||
int startIdx = p * numClasses;
|
||||
for (int c = 0; c < numClasses; ++c)
|
||||
{
|
||||
labelScores[c].push_back(confData[startIdx + c]);
|
||||
}
|
||||
}
|
||||
confData += numPredsPerClass * numClasses;
|
||||
}
|
||||
}
|
||||
|
||||
// Do non maximum suppression given bboxes and scores.
|
||||
// Inspired by Piotr Dollar's NMS implementation in EdgeBox.
|
||||
// https://goo.gl/jV3JYS
|
||||
// bboxes: a set of bounding boxes.
|
||||
// scores: a set of corresponding confidences.
|
||||
// score_threshold: a threshold used to filter detection results.
|
||||
// nms_threshold: a threshold used in non maximum suppression.
|
||||
// top_k: if not -1, keep at most top_k picked indices.
|
||||
// indices: the kept indices of bboxes after nms.
|
||||
void ApplyNMSFast(const std::vector<caffe::NormalizedBBox>& bboxes,
|
||||
const std::vector<float>& scores, const float score_threshold,
|
||||
const float nms_threshold, const float eta, const int top_k,
|
||||
std::vector<int>* indices) {
|
||||
// Sanity check.
|
||||
CV_Assert(bboxes.size() == scores.size());
|
||||
|
||||
// Get top_k scores (with corresponding indices).
|
||||
std::vector<std::pair<float, int> > score_index_vec;
|
||||
GetMaxScoreIndex(scores, score_threshold, top_k, &score_index_vec);
|
||||
|
||||
// Do nms.
|
||||
float adaptive_threshold = nms_threshold;
|
||||
indices->clear();
|
||||
while (score_index_vec.size() != 0) {
|
||||
const int idx = score_index_vec.front().second;
|
||||
bool keep = true;
|
||||
for (int k = 0; k < indices->size(); ++k) {
|
||||
if (keep) {
|
||||
const int kept_idx = (*indices)[k];
|
||||
float overlap = JaccardOverlap(bboxes[idx], bboxes[kept_idx]);
|
||||
keep = overlap <= adaptive_threshold;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (keep) {
|
||||
indices->push_back(idx);
|
||||
}
|
||||
score_index_vec.erase(score_index_vec.begin());
|
||||
if (keep && eta < 1 && adaptive_threshold > 0.5) {
|
||||
adaptive_threshold *= eta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get max scores with corresponding indices.
|
||||
// scores: a set of scores.
|
||||
// threshold: only consider scores higher than the threshold.
|
||||
// top_k: if -1, keep all; otherwise, keep at most top_k.
|
||||
// score_index_vec: store the sorted (score, index) pair.
|
||||
void GetMaxScoreIndex(const std::vector<float>& scores, const float threshold,const int top_k,
|
||||
std::vector<std::pair<float, int> >* score_index_vec)
|
||||
{
|
||||
// Generate index score pairs.
|
||||
for (size_t i = 0; i < scores.size(); ++i)
|
||||
{
|
||||
if (scores[i] > threshold)
|
||||
{
|
||||
score_index_vec->push_back(std::make_pair(scores[i], i));
|
||||
}
|
||||
}
|
||||
|
||||
// Sort the score pair according to the scores in descending order
|
||||
std::stable_sort(score_index_vec->begin(), score_index_vec->end(),
|
||||
util::SortScorePairDescend<int>);
|
||||
|
||||
// Keep top_k scores if needed.
|
||||
if (top_k > -1 && top_k < (int)score_index_vec->size())
|
||||
{
|
||||
score_index_vec->resize(top_k);
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the intersection between two bboxes.
|
||||
void IntersectBBox(const caffe::NormalizedBBox& bbox1,
|
||||
const caffe::NormalizedBBox& bbox2,
|
||||
caffe::NormalizedBBox* intersect_bbox) {
|
||||
if (bbox2.xmin() > bbox1.xmax() || bbox2.xmax() < bbox1.xmin() ||
|
||||
bbox2.ymin() > bbox1.ymax() || bbox2.ymax() < bbox1.ymin())
|
||||
{
|
||||
// Return [0, 0, 0, 0] if there is no intersection.
|
||||
intersect_bbox->set_xmin(0);
|
||||
intersect_bbox->set_ymin(0);
|
||||
intersect_bbox->set_xmax(0);
|
||||
intersect_bbox->set_ymax(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
intersect_bbox->set_xmin(std::max(bbox1.xmin(), bbox2.xmin()));
|
||||
intersect_bbox->set_ymin(std::max(bbox1.ymin(), bbox2.ymin()));
|
||||
intersect_bbox->set_xmax(std::min(bbox1.xmax(), bbox2.xmax()));
|
||||
intersect_bbox->set_ymax(std::min(bbox1.ymax(), bbox2.ymax()));
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the jaccard (intersection over union IoU) overlap between two bboxes.
|
||||
float JaccardOverlap(const caffe::NormalizedBBox& bbox1,
|
||||
const caffe::NormalizedBBox& bbox2,
|
||||
const bool normalized=true)
|
||||
{
|
||||
caffe::NormalizedBBox intersect_bbox;
|
||||
IntersectBBox(bbox1, bbox2, &intersect_bbox);
|
||||
float intersect_width, intersect_height;
|
||||
if (normalized)
|
||||
{
|
||||
intersect_width = intersect_bbox.xmax() - intersect_bbox.xmin();
|
||||
intersect_height = intersect_bbox.ymax() - intersect_bbox.ymin();
|
||||
}
|
||||
else
|
||||
{
|
||||
intersect_width = intersect_bbox.xmax() - intersect_bbox.xmin() + 1;
|
||||
intersect_height = intersect_bbox.ymax() - intersect_bbox.ymin() + 1;
|
||||
}
|
||||
if (intersect_width > 0 && intersect_height > 0)
|
||||
{
|
||||
float intersect_size = intersect_width * intersect_height;
|
||||
float bbox1_size = BBoxSize(bbox1);
|
||||
float bbox2_size = BBoxSize(bbox2);
|
||||
return intersect_size / (bbox1_size + bbox2_size - intersect_size);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const std::string DetectionOutputLayerImpl::_layerName = std::string("DetectionOutput");
|
||||
|
||||
Ptr<DetectionOutputLayer> DetectionOutputLayer::create(const LayerParams ¶ms)
|
||||
{
|
||||
return Ptr<DetectionOutputLayer>(new DetectionOutputLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,502 @@
|
||||
#include "../precomp.hpp"
|
||||
#include "op_halide.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
using std::abs;
|
||||
using std::exp;
|
||||
using std::tanh;
|
||||
using std::pow;
|
||||
|
||||
template<typename Func>
|
||||
class ElementWiseLayer : public Func::Layer
|
||||
{
|
||||
public:
|
||||
class PBody : public cv::ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
const Func* func_;
|
||||
const Mat* src_;
|
||||
Mat* dst_;
|
||||
int nstripes_;
|
||||
|
||||
PBody(const Func &func, const Mat &src, Mat& dst, int nstripes)
|
||||
{
|
||||
func_ = &func;
|
||||
src_ = &src;
|
||||
dst_ = &dst;
|
||||
nstripes_ = nstripes;
|
||||
}
|
||||
|
||||
void operator()(const Range &r) const
|
||||
{
|
||||
int nstripes = nstripes_, nsamples, outCn;
|
||||
size_t planeSize;
|
||||
|
||||
if( src_->dims == 4 )
|
||||
{
|
||||
nsamples = src_->size[0];
|
||||
outCn = src_->size[1];
|
||||
planeSize = (size_t)src_->size[2]*src_->size[3];
|
||||
}
|
||||
else
|
||||
{
|
||||
nsamples = outCn = 1;
|
||||
planeSize = (size_t)src_->total();
|
||||
}
|
||||
|
||||
size_t stripeSize = (planeSize + nstripes - 1)/nstripes;
|
||||
size_t stripeStart = r.start*stripeSize;
|
||||
size_t stripeEnd = std::min(r.end*stripeSize, planeSize);
|
||||
|
||||
for( int i = 0; i < nsamples; i++ )
|
||||
{
|
||||
const float* srcptr = src_->ptr<float>(i) + stripeStart;
|
||||
float* dstptr = dst_->ptr<float>(i) + stripeStart;
|
||||
func_->apply(srcptr, dstptr, (int)(stripeEnd - stripeStart), planeSize, 0, outCn);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ElementWiseLayer(const Func &f=Func()) { func = f; }
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide();
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> tryAttach(const Ptr<BackendNode>& node)
|
||||
{
|
||||
switch (node->backendId)
|
||||
{
|
||||
case DNN_BACKEND_HALIDE:
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
auto base = node.dynamicCast<HalideBackendNode>();
|
||||
Halide::Func& input = base->funcs.back();
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (this->name.empty() ? Halide::Func() : Halide::Func(this->name));
|
||||
func.attachHalide(input(x, y, c, n), top);
|
||||
return Ptr<BackendNode>(new HalideBackendNode(base, top));
|
||||
#endif // HAVE_HALIDE
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
Halide::Buffer<float> input = halideBuffer(inputs[0]);
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (this->name.empty() ? Halide::Func() : Halide::Func(this->name));
|
||||
func.attachHalide(input(x, y, c, n), top);
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
|
||||
return true;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
const Mat &src = *inputs[i];
|
||||
Mat &dst = outputs[i];
|
||||
CV_Assert(src.size == dst.size && src.type() == dst.type() &&
|
||||
src.isContinuous() && dst.isContinuous() && src.type() == CV_32F);
|
||||
|
||||
const int nstripes = getNumThreads();
|
||||
PBody body(func, src, dst, nstripes);
|
||||
parallel_for_(Range(0, nstripes), body, nstripes);
|
||||
}
|
||||
}
|
||||
|
||||
void forwardSlice(const float* src, float* dst, int len, size_t planeSize, int cn0, int cn1) const
|
||||
{
|
||||
func.apply(src, dst, len, planeSize, cn0, cn1);
|
||||
}
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
long flops = 0;
|
||||
for (int i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
flops += total(outputs[i]) * func.getFLOPSPerElement();
|
||||
}
|
||||
return flops;
|
||||
}
|
||||
|
||||
Func func;
|
||||
bool run_parallel;
|
||||
};
|
||||
|
||||
struct ReLUFunctor
|
||||
{
|
||||
typedef ReLULayer Layer;
|
||||
float slope;
|
||||
|
||||
explicit ReLUFunctor(float slope_=1.f) : slope(slope_) {}
|
||||
|
||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||
{
|
||||
float s = slope;
|
||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||
{
|
||||
int i = 0;
|
||||
#if CV_SIMD128
|
||||
v_float32x4 s4 = v_setall_f32(s), z = v_setzero_f32();
|
||||
for( ; i <= len - 16; i += 16 )
|
||||
{
|
||||
v_float32x4 x0 = v_load(srcptr + i);
|
||||
v_float32x4 x1 = v_load(srcptr + i + 4);
|
||||
v_float32x4 x2 = v_load(srcptr + i + 8);
|
||||
v_float32x4 x3 = v_load(srcptr + i + 12);
|
||||
x0 = v_select(x0 >= z, x0, x0*s4);
|
||||
x1 = v_select(x1 >= z, x1, x1*s4);
|
||||
x2 = v_select(x2 >= z, x2, x2*s4);
|
||||
x3 = v_select(x3 >= z, x3, x3*s4);
|
||||
v_store(dstptr + i, x0);
|
||||
v_store(dstptr + i + 4, x1);
|
||||
v_store(dstptr + i + 8, x2);
|
||||
v_store(dstptr + i + 12, x3);
|
||||
}
|
||||
#endif
|
||||
for( ; i < len; i++ )
|
||||
{
|
||||
float x = srcptr[i];
|
||||
dstptr[i] = x >= 0.f ? x : s*x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
void attachHalide(const Halide::Expr& input, Halide::Func& top)
|
||||
{
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
if (slope)
|
||||
{
|
||||
top(x, y, c, n) = select(input >= 0.0f, input, slope);
|
||||
}
|
||||
else
|
||||
{
|
||||
top(x, y, c, n) = max(input, 0.0f);
|
||||
}
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
int64 getFLOPSPerElement() const { return 1; }
|
||||
};
|
||||
|
||||
struct TanHFunctor
|
||||
{
|
||||
typedef TanHLayer Layer;
|
||||
|
||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||
{
|
||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||
{
|
||||
for( int i = 0; i < len; i++ )
|
||||
{
|
||||
float x = srcptr[i];
|
||||
dstptr[i] = tanh(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
void attachHalide(const Halide::Expr& input, Halide::Func& top)
|
||||
{
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
top(x, y, c, n) = tanh(input);
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
int64 getFLOPSPerElement() const { return 1; }
|
||||
};
|
||||
|
||||
struct SigmoidFunctor
|
||||
{
|
||||
typedef SigmoidLayer Layer;
|
||||
|
||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||
{
|
||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||
{
|
||||
for( int i = 0; i < len; i++ )
|
||||
{
|
||||
float x = srcptr[i];
|
||||
dstptr[i] = 1.f/(1.f + exp(-x));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
void attachHalide(const Halide::Expr& input, Halide::Func& top)
|
||||
{
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
top(x, y, c, n) = 1.0f / (1.0f + exp(-input));
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
int64 getFLOPSPerElement() const { return 3; }
|
||||
};
|
||||
|
||||
struct AbsValFunctor
|
||||
{
|
||||
typedef AbsLayer Layer;
|
||||
|
||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||
{
|
||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||
{
|
||||
for( int i = 0; i < len; i++ )
|
||||
{
|
||||
float x = srcptr[i];
|
||||
dstptr[i] = abs(x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
void attachHalide(const Halide::Expr& input, Halide::Func& top)
|
||||
{
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
top(x, y, c, n) = abs(input);
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
int64 getFLOPSPerElement() const { return 1; }
|
||||
};
|
||||
|
||||
struct BNLLFunctor
|
||||
{
|
||||
typedef BNLLLayer Layer;
|
||||
|
||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||
{
|
||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||
{
|
||||
for( int i = 0; i < len; i++ )
|
||||
{
|
||||
float x = srcptr[i];
|
||||
dstptr[i] = log(1.f + exp(-abs(x)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
void attachHalide(const Halide::Expr& input, Halide::Func& top)
|
||||
{
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
top(x, y, c, n) = log(1.0f + exp(-abs(input)));
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
int64 getFLOPSPerElement() const { return 5; }
|
||||
};
|
||||
|
||||
struct PowerFunctor
|
||||
{
|
||||
typedef PowerLayer Layer;
|
||||
|
||||
float power;
|
||||
float scale;
|
||||
float shift;
|
||||
|
||||
explicit PowerFunctor(float power_ = 1.f, float scale_ = 1.f, float shift_ = 0.f)
|
||||
: power(power_), scale(scale_), shift(shift_) {}
|
||||
|
||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||
{
|
||||
float a = scale, b = shift, p = power;
|
||||
if( p == 1.f )
|
||||
{
|
||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||
{
|
||||
for( int i = 0; i < len; i++ )
|
||||
{
|
||||
float x = srcptr[i];
|
||||
dstptr[i] = a*x + b;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||
{
|
||||
for( int i = 0; i < len; i++ )
|
||||
{
|
||||
float x = srcptr[i];
|
||||
dstptr[i] = pow(a*x + b, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
void attachHalide(const Halide::Expr& input, Halide::Func& top)
|
||||
{
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Expr topExpr = (scale == 1.0f ? input : input * scale);
|
||||
if (shift)
|
||||
{
|
||||
topExpr += shift;
|
||||
}
|
||||
if (power != 1.0f)
|
||||
{
|
||||
topExpr = pow(topExpr, power);
|
||||
}
|
||||
top(x, y, c, n) = topExpr;
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
int64 getFLOPSPerElement() const { return power == 1 ? 2 : 10; }
|
||||
};
|
||||
|
||||
|
||||
struct ChannelsPReLUFunctor
|
||||
{
|
||||
typedef ChannelsPReLULayer Layer;
|
||||
Mat scale;
|
||||
|
||||
explicit ChannelsPReLUFunctor(const Mat& scale_=Mat()) : scale(scale_)
|
||||
{
|
||||
}
|
||||
|
||||
void apply(const float* srcptr, float* dstptr, int len, size_t planeSize, int cn0, int cn1) const
|
||||
{
|
||||
CV_Assert(scale.isContinuous() && scale.type() == CV_32F);
|
||||
|
||||
const float* scaleptr = scale.ptr<float>();
|
||||
CV_Assert( 0 <= cn0 && cn0 < cn1 && cn1 <= (int)scale.total() );
|
||||
|
||||
for( int cn = cn0; cn < cn1; cn++, srcptr += planeSize, dstptr += planeSize )
|
||||
{
|
||||
float s = scaleptr[cn];
|
||||
int i = 0;
|
||||
#if CV_SIMD128
|
||||
v_float32x4 s4 = v_setall_f32(s), z = v_setzero_f32();
|
||||
for( ; i <= len - 16; i += 16 )
|
||||
{
|
||||
v_float32x4 x0 = v_load(ptr + i);
|
||||
v_float32x4 x1 = v_load(ptr + i + 4);
|
||||
v_float32x4 x2 = v_load(ptr + i + 8);
|
||||
v_float32x4 x3 = v_load(ptr + i + 12);
|
||||
x0 = v_select(x0 >= z, x0, x0*s4);
|
||||
x1 = v_select(x1 >= z, x1, x1*s4);
|
||||
x2 = v_select(x2 >= z, x2, x2*s4);
|
||||
x3 = v_select(x3 >= z, x3, x3*s4);
|
||||
v_store(ptr + i, x0);
|
||||
v_store(ptr + i + 4, x1);
|
||||
v_store(ptr + i + 8, x2);
|
||||
v_store(ptr + i + 12, x3);
|
||||
}
|
||||
#endif
|
||||
for( ; i < len; i++ )
|
||||
{
|
||||
float x = srcptr[i];
|
||||
dstptr[i] = x >= 0.f ? x : s*x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
void attachHalide(const Halide::Expr& input, Halide::Func& top)
|
||||
{
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
auto weights = wrapToHalideBuffer(scale, {(int)scale.total()});
|
||||
top(x, y, c, n) = select(input >= 0.0f, input, weights(c) * input);
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
int64 getFLOPSPerElement() const { return 1; }
|
||||
};
|
||||
|
||||
#define ACTIVATION_CREATOR_FOR(_Layer, _Functor, ...) \
|
||||
Ptr<_Layer> _Layer::create() { \
|
||||
return return Ptr<_Layer>( new ElementWiseLayer<_Functor>(_Functor()) ); }
|
||||
|
||||
|
||||
Ptr<ReLULayer> ReLULayer::create(const LayerParams& params)
|
||||
{
|
||||
float negativeSlope = params.get<float>("negative_slope", 0.f);
|
||||
Ptr<ReLULayer> l(new ElementWiseLayer<ReLUFunctor>(ReLUFunctor(negativeSlope)));
|
||||
l->setParamsFrom(params);
|
||||
l->negativeSlope = negativeSlope;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
Ptr<TanHLayer> TanHLayer::create(const LayerParams& params)
|
||||
{
|
||||
Ptr<TanHLayer> l(new ElementWiseLayer<TanHFunctor>());
|
||||
l->setParamsFrom(params);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
Ptr<SigmoidLayer> SigmoidLayer::create(const LayerParams& params)
|
||||
{
|
||||
Ptr<SigmoidLayer> l(new ElementWiseLayer<SigmoidFunctor>());
|
||||
l->setParamsFrom(params);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
Ptr<AbsLayer> AbsLayer::create(const LayerParams& params)
|
||||
{
|
||||
Ptr<AbsLayer> l(new ElementWiseLayer<AbsValFunctor>());
|
||||
l->setParamsFrom(params);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
Ptr<BNLLLayer> BNLLLayer::create(const LayerParams& params)
|
||||
{
|
||||
Ptr<BNLLLayer> l(new ElementWiseLayer<BNLLFunctor>());
|
||||
l->setParamsFrom(params);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
Ptr<PowerLayer> PowerLayer::create(const LayerParams& params)
|
||||
{
|
||||
float power = params.get<float>("power", 1.0f);
|
||||
float scale = params.get<float>("scale", 1.0f);
|
||||
float shift = params.get<float>("shift", 0.0f);
|
||||
Ptr<PowerLayer> l(new ElementWiseLayer<PowerFunctor>(PowerFunctor(power, scale, shift)));
|
||||
l->setParamsFrom(params);
|
||||
l->power = power;
|
||||
l->scale = scale;
|
||||
l->shift = shift;
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
Ptr<ChannelsPReLULayer> ChannelsPReLULayer::create(const LayerParams& params)
|
||||
{
|
||||
Ptr<ChannelsPReLULayer> l(new ElementWiseLayer<ChannelsPReLUFunctor>(ChannelsPReLUFunctor(params.blobs[0])));
|
||||
l->setParamsFrom(params);
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "op_halide.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class EltwiseLayerImpl : public EltwiseLayer
|
||||
{
|
||||
public:
|
||||
EltwiseOp op;
|
||||
std::vector<int> coeffs;
|
||||
|
||||
EltwiseLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
op = EltwiseLayer::SUM;
|
||||
if (params.has("operation"))
|
||||
{
|
||||
String operation = params.get<String>("operation").toLowerCase();
|
||||
if (operation == "prod")
|
||||
op = EltwiseLayer::PROD;
|
||||
else if (operation == "sum")
|
||||
op = EltwiseLayer::SUM;
|
||||
else if (operation == "max")
|
||||
op = EltwiseLayer::MAX;
|
||||
else
|
||||
CV_Error(cv::Error::StsBadArg, "Unknown operaticon type \"" + operation + "\"");
|
||||
}
|
||||
|
||||
if (params.has("coeff"))
|
||||
{
|
||||
DictValue paramCoeff = params.get("coeff");
|
||||
int i, n = paramCoeff.size();
|
||||
coeffs.resize(n);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
coeffs[i] = paramCoeff.get<int>(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide();
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() >= 2);
|
||||
CV_Assert(coeffs.size() == 0 || coeffs.size() == inputs.size());
|
||||
CV_Assert(op == SUM || coeffs.size() == 0);
|
||||
|
||||
for (int i = 1; i < inputs.size(); i++)
|
||||
{
|
||||
CV_Assert(inputs[0] == inputs[i]);
|
||||
}
|
||||
|
||||
outputs.assign(1, inputs[0]);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat *> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
Mat& output = outputs[0];
|
||||
switch (op)
|
||||
{
|
||||
case SUM:
|
||||
CV_Assert(coeffs.size() == 0 || coeffs.size() == inputs.size());
|
||||
if (0 < coeffs.size())
|
||||
{
|
||||
output.setTo(0.);
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
output += *inputs[i] * coeffs[i];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
add(*inputs[0], *inputs[1], output);
|
||||
for (size_t i = 2; i < inputs.size(); i++)
|
||||
{
|
||||
output += *inputs[i];
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PROD:
|
||||
output.setTo(1.);
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
output = output.mul(*inputs[i]);
|
||||
}
|
||||
break;
|
||||
case MAX:
|
||||
cv::max(*inputs[0], *inputs[1], output);
|
||||
for (size_t i = 2; i < inputs.size(); i++)
|
||||
{
|
||||
cv::max(output, *inputs[i], output);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
CV_Assert(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &input)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::Expr topExpr;
|
||||
std::vector<Halide::Buffer<> > inputBuffers = halideBuffers(input);
|
||||
switch (op)
|
||||
{
|
||||
case SUM:
|
||||
if (coeffs.empty())
|
||||
{
|
||||
topExpr = inputBuffers[0](x, y, c, n) +
|
||||
inputBuffers[1](x, y, c, n);
|
||||
for (int i = 2; i < inputBuffers.size(); ++i)
|
||||
topExpr += inputBuffers[i](x, y, c, n);
|
||||
}
|
||||
else
|
||||
{
|
||||
topExpr = coeffs[0] * inputBuffers[0](x, y, c, n) +
|
||||
coeffs[1] * inputBuffers[1](x, y, c, n);
|
||||
for (int i = 2; i < inputBuffers.size(); ++i)
|
||||
topExpr += coeffs[i] * inputBuffers[i](x, y, c, n);
|
||||
}
|
||||
break;
|
||||
case PROD:
|
||||
topExpr = inputBuffers[0](x, y, c, n) *
|
||||
inputBuffers[1](x, y, c, n);
|
||||
for (int i = 2; i < inputBuffers.size(); ++i)
|
||||
topExpr *= inputBuffers[i](x, y, c, n);
|
||||
break;
|
||||
case MAX:
|
||||
topExpr = max(inputBuffers[0](x, y, c, n),
|
||||
inputBuffers[1](x, y, c, n));
|
||||
for (int i = 2; i < inputBuffers.size(); ++i)
|
||||
topExpr = max(topExpr, inputBuffers[i](x, y, c, n));
|
||||
break;
|
||||
default:
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
top(x, y, c, n) = topExpr;
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)outputs; // suppress unused variable warning
|
||||
CV_Assert(inputs.size());
|
||||
|
||||
long flops = inputs.size() * total(inputs[0]);
|
||||
|
||||
return flops;
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<EltwiseLayer> EltwiseLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<EltwiseLayer>(new EltwiseLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include <float.h>
|
||||
#include <algorithm>
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class FlattenLayerImpl : public FlattenLayer
|
||||
{
|
||||
public:
|
||||
FlattenLayerImpl(const LayerParams ¶ms)
|
||||
{
|
||||
_startAxis = params.get<int>("axis", 1);
|
||||
_endAxis = params.get<int>("end_axis", -1);
|
||||
setParamsFrom(params);
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() > 0);
|
||||
for (size_t i = 1; i < inputs.size(); i++)
|
||||
{
|
||||
CV_Assert(inputs[i] == inputs[0]);
|
||||
}
|
||||
|
||||
int numAxes = inputs[0].size();
|
||||
int startAxis = clamp(_startAxis, numAxes);
|
||||
int endAxis = clamp(_endAxis, numAxes);
|
||||
|
||||
for (size_t i = 1; i < inputs.size(); i++)
|
||||
{
|
||||
CV_Assert(inputs[i] == inputs[0]);
|
||||
}
|
||||
|
||||
|
||||
CV_Assert(startAxis >= 0);
|
||||
CV_Assert(endAxis >= startAxis && endAxis < (int)numAxes);
|
||||
|
||||
size_t flattenedDimensionSize = total(inputs[0], startAxis, endAxis + 1);
|
||||
|
||||
MatShape outputShapeVec;
|
||||
for (int i = 0; i < startAxis; i++)
|
||||
{
|
||||
outputShapeVec.push_back(inputs[0][i]);
|
||||
}
|
||||
outputShapeVec.push_back(flattenedDimensionSize);
|
||||
for (size_t i = endAxis + 1; i < numAxes; i++)
|
||||
{
|
||||
outputShapeVec.push_back(inputs[0][i]);
|
||||
}
|
||||
CV_Assert(outputShapeVec.size() <= 4);
|
||||
|
||||
outputs.resize(inputs.size(), outputShapeVec);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
MatShape outShape = shape(outputs[i]);
|
||||
outputs[i] = inputs[i]->reshape(1, (int)outShape.size(), &outShape[0]);
|
||||
}
|
||||
}
|
||||
|
||||
int _startAxis;
|
||||
int _endAxis;
|
||||
};
|
||||
|
||||
Ptr<FlattenLayer> FlattenLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<FlattenLayer>(new FlattenLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "op_halide.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class FullyConnectedLayerImpl : public InnerProductLayer
|
||||
{
|
||||
public:
|
||||
enum { VEC_ALIGN = 8 };
|
||||
|
||||
FullyConnectedLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
CV_Assert(1 <= blobs.size() && blobs.size() <= 2);
|
||||
|
||||
int numOutput = params.get<int>("num_output");
|
||||
int innerSize = (int)blobs[0].total() / numOutput;
|
||||
bias = params.get<bool>("bias_term", true);
|
||||
axis = params.get<int>("axis", 1);
|
||||
|
||||
CV_Assert(blobs[0].dims >= 2 && (size_t)(innerSize * numOutput) == blobs[0].total());
|
||||
CV_Assert(!bias || (blobs.size() == 2 && (size_t)numOutput == blobs[1].total()));
|
||||
|
||||
weightsMat = blobs[0] = blobs[0].reshape(1, numOutput);
|
||||
int vecsize = weightsMat.cols;
|
||||
if( vecsize % VEC_ALIGN != 0 )
|
||||
{
|
||||
int vecsize_aligned = (int)alignSize(vecsize, VEC_ALIGN);
|
||||
Mat weightsBuf(weightsMat.rows, vecsize_aligned, weightsMat.type());
|
||||
Mat wpadding = weightsBuf.colRange(vecsize, vecsize_aligned);
|
||||
wpadding.setTo(Scalar::all(0.));
|
||||
weightsMat = weightsBuf.colRange(0, vecsize);
|
||||
blobs[0].copyTo(weightsMat);
|
||||
blobs[0] = weightsMat;
|
||||
}
|
||||
|
||||
if (bias)
|
||||
biasMat = blobs[1] = blobs[1].reshape(1, 1);
|
||||
else
|
||||
biasMat = Mat::zeros(1, numOutput, weightsMat.type());
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &) const
|
||||
{
|
||||
CV_Assert(inputs.size() > 0);
|
||||
CV_Assert(1 <= blobs.size() && blobs.size() <= 2);
|
||||
CV_Assert(blobs[0].dims == 2);
|
||||
|
||||
int cAxis = clamp(axis, inputs[0]);
|
||||
int outerSize = total(inputs[0], 0, cAxis);
|
||||
int numOutput = blobs[0].size[0];
|
||||
outputs.resize(inputs.size(), shape(outerSize, numOutput));
|
||||
|
||||
CV_Assert(!bias || (size_t)numOutput == blobs[1].total());
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide() && axis == 1;
|
||||
}
|
||||
|
||||
class FullConnected : public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
FullConnected(const Mat& srcMat, const Mat& weights, const Mat& biasMat, Mat& dstMat, int nstripes)
|
||||
{
|
||||
CV_Assert( srcMat.dims == 2 && srcMat.cols == weights.cols &&
|
||||
dstMat.rows == srcMat.rows && dstMat.cols == weights.rows &&
|
||||
srcMat.type() == weights.type() && weights.type() == dstMat.type() &&
|
||||
srcMat.type() == CV_32F &&
|
||||
(biasMat.empty() || (biasMat.type() == srcMat.type() &&
|
||||
biasMat.isContinuous() && (int)biasMat.total() == dstMat.cols)) );
|
||||
|
||||
srcMat_ = &srcMat;
|
||||
weights_ = &weights;
|
||||
biasMat_ = &biasMat;
|
||||
dstMat_ = &dstMat;
|
||||
nstripes_ = nstripes;
|
||||
useAVX2_ = checkHardwareSupport(CPU_AVX2);
|
||||
}
|
||||
|
||||
void operator()(const Range& r) const
|
||||
{
|
||||
int valign = FullyConnectedLayerImpl::VEC_ALIGN;
|
||||
int nsamples = srcMat_->rows;
|
||||
int nw0 = weights_->rows;
|
||||
int k, vecsize = srcMat_->cols;
|
||||
int vecsize_aligned = (int)alignSize(vecsize, VEC_ALIGN);
|
||||
int nstripes = nstripes_;
|
||||
size_t total = (size_t)nsamples*nw0;
|
||||
size_t stripeSize = (total + nstripes - 1)/nstripes;
|
||||
size_t stripeStart = r.start*stripeSize;
|
||||
size_t stripeEnd = r.end == nstripes ? total : std::min(r.end*stripeSize, total);
|
||||
size_t wstep = weights_->step1();
|
||||
AutoBuffer<float> srcbuf(vecsize_aligned + valign);
|
||||
float* sptr = alignPtr((float*)srcbuf, (int)(valign*sizeof(float)));
|
||||
|
||||
for( k = vecsize; k < vecsize_aligned; k++ )
|
||||
sptr[k] = 0.f;
|
||||
|
||||
for( size_t ofs = stripeStart; ofs < stripeEnd; )
|
||||
{
|
||||
int sampleIdx = (int)(ofs / nw0);
|
||||
int delta = (int)(ofs - (size_t)sampleIdx*nw0);
|
||||
const float* sptr_ = srcMat_->ptr<float>(sampleIdx);
|
||||
const float* wptr = weights_->ptr<float>(delta);
|
||||
float* dptr = dstMat_->ptr<float>(sampleIdx) + delta;
|
||||
const float* biasptr = biasMat_->ptr<float>() + delta;
|
||||
int nw = std::min(nw0 - delta, (int)(stripeEnd - ofs));
|
||||
|
||||
memcpy(sptr, sptr_, vecsize*sizeof(sptr[0]));
|
||||
|
||||
#if CV_DNN_TRY_AVX2
|
||||
if( useAVX2_ )
|
||||
fastGEMM1T_avx2( sptr, wptr, wstep, biasptr, dptr, nw, vecsize);
|
||||
else
|
||||
#endif
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
#if CV_SIMD128
|
||||
for( ; i <= nw - 4; i += 4, wptr += 4*wstep )
|
||||
{
|
||||
vfloat32x4 vs0 = v_setall_f32(0.f), vs1 = v_setall_f32(0.f);
|
||||
vfloat32x4 vs2 = v_setall_f32(0.f), vs3 = v_setall_f32(0.f);
|
||||
|
||||
for( k = 0; k < vecsize; k += 4 )
|
||||
{
|
||||
vfloat32x4 v = v_load_aligned(sptr + k);
|
||||
vs0 += v*v_load_aligned(wptr + k);
|
||||
vs1 += v*v_load_aligned(wptr + wstep + k);
|
||||
vs2 += v*v_load_aligned(wptr + wstep*2 + k);
|
||||
vs3 += v*v_load_aligned(wptr + wstep*3 + k);
|
||||
}
|
||||
|
||||
vfloat32x4 s = v_reduce_sum4(vs0, vs1, vs2, vs3);
|
||||
s += v_load(biasptr + i);
|
||||
v_store(dptr + i, s);
|
||||
}
|
||||
#endif
|
||||
|
||||
for( ; i < nw; i++, wptr += wstep )
|
||||
{
|
||||
float s0=biasptr[i];
|
||||
|
||||
for( k = 0; k < vecsize; k++ )
|
||||
{
|
||||
float v = sptr[k];
|
||||
s0 += v*wptr[k];
|
||||
}
|
||||
dptr[i] = s0;
|
||||
}
|
||||
}
|
||||
ofs += nw;
|
||||
}
|
||||
}
|
||||
|
||||
const Mat *srcMat_, *weights_, *biasMat_;
|
||||
Mat* dstMat_;
|
||||
int nstripes_;
|
||||
bool useAVX2_;
|
||||
};
|
||||
|
||||
void forward(std::vector<Mat*> &input, std::vector<Mat> &output, std::vector<Mat> &)
|
||||
{
|
||||
int axisCan = clamp(axis, input[0]->dims);
|
||||
int outerSize = input[0]->total(0, axisCan);
|
||||
|
||||
for (size_t i = 0; i < input.size(); i++)
|
||||
{
|
||||
Mat srcMat = input[i]->reshape(1, outerSize);
|
||||
Mat dstMat = output[i].reshape(1, outerSize);
|
||||
|
||||
const int nstripes = getNumThreads();
|
||||
FullConnected fconn(srcMat, weightsMat, biasMat, dstMat, nstripes);
|
||||
parallel_for_(Range(0, nstripes), fconn, nstripes);
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
int inW, inH, inC, inN, outC = blobs[0].size[0];
|
||||
Halide::Buffer<float> inputBuffer = halideBuffer(inputs[0]);
|
||||
getCanonicalSize(inputBuffer, &inW, &inH, &inC, &inN);
|
||||
auto weights = wrapToHalideBuffer(blobs[0], {inW, inH, inC, outC});
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::RDom r(0, inW, 0, inH, 0, inC);
|
||||
Halide::Expr topExpr = sum(inputBuffer(r.x, r.y, r.z, n) *
|
||||
weights(r.x, r.y, r.z, c));
|
||||
if (bias)
|
||||
{
|
||||
Halide::Buffer<float> bias = wrapToHalideBuffer(blobs[1], {outC});
|
||||
topExpr += bias(c);
|
||||
}
|
||||
top(x, y, c, n) = topExpr;
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)inputs; // suppress unused variable warning
|
||||
long flops = 0;
|
||||
|
||||
int innerSize = blobs[0].size[1];
|
||||
for(int i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
flops += 3*innerSize*total(outputs[i]);
|
||||
}
|
||||
|
||||
return flops;
|
||||
|
||||
}
|
||||
|
||||
bool bias;
|
||||
Mat weightsMat, biasMat;
|
||||
};
|
||||
|
||||
Ptr<InnerProductLayer> InnerProductLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<InnerProductLayer>(new FullyConnectedLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,356 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
|
||||
#if CV_DNN_TRY_AVX2
|
||||
|
||||
#include <immintrin.h>
|
||||
|
||||
namespace cv {
|
||||
namespace dnn {
|
||||
|
||||
void fastConv_avx2( const float* weights, size_t wstep, const float* bias,
|
||||
const float* rowbuf, float* output, const int* outShape,
|
||||
int blockSize, int vecsize, int vecsize_aligned,
|
||||
const float* relu, bool initOutput )
|
||||
{
|
||||
int outCn = outShape[1];
|
||||
size_t outPlaneSize = outShape[2]*outShape[3];
|
||||
float r0 = 1.f, r1 = 1.f, r2 = 1.f;
|
||||
__m256 vr0 = _mm256_set1_ps(1.f), vr1 = vr0, vr2 = vr0, z = _mm256_setzero_ps();
|
||||
|
||||
// now compute dot product of the weights
|
||||
// and im2row-transformed part of the tensor
|
||||
for( int i = 0; i < outCn; i += 3 )
|
||||
{
|
||||
const float* wptr0 = weights + i*wstep;
|
||||
const float* wptr1 = wptr0 + wstep;
|
||||
const float* wptr2 = wptr1 + wstep;
|
||||
float* outptr0 = output + i*outPlaneSize;
|
||||
float* outptr1 = outptr0 + outPlaneSize;
|
||||
float* outptr2 = outptr1 + outPlaneSize;
|
||||
float bias0 = bias[i], bias1 = bias[i+1], bias2 = bias[i+2];
|
||||
|
||||
if( i+2 >= outCn )
|
||||
{
|
||||
wptr2 = wptr1;
|
||||
outptr2 = outptr1;
|
||||
bias2 = bias1;
|
||||
if( i+1 >= outCn )
|
||||
{
|
||||
wptr2 = wptr1 = wptr0;
|
||||
outptr2 = outptr1 = outptr0;
|
||||
bias2 = bias1 = bias0;
|
||||
}
|
||||
}
|
||||
|
||||
if( relu )
|
||||
{
|
||||
r0 = relu[i];
|
||||
r1 = relu[i+1];
|
||||
r2 = relu[i+2];
|
||||
vr0 = _mm256_set1_ps(r0);
|
||||
vr1 = _mm256_set1_ps(r1);
|
||||
vr2 = _mm256_set1_ps(r2);
|
||||
}
|
||||
|
||||
int j = 0;
|
||||
for( ; j <= blockSize - 4; j += 4 )
|
||||
{
|
||||
const float* rptr = rowbuf + j*vecsize_aligned;
|
||||
|
||||
__m256 vs00 = _mm256_setzero_ps(), vs01 = _mm256_setzero_ps(),
|
||||
vs02 = _mm256_setzero_ps(), vs03 = _mm256_setzero_ps(),
|
||||
vs10 = _mm256_setzero_ps(), vs11 = _mm256_setzero_ps(),
|
||||
vs12 = _mm256_setzero_ps(), vs13 = _mm256_setzero_ps(),
|
||||
vs20 = _mm256_setzero_ps(), vs21 = _mm256_setzero_ps(),
|
||||
vs22 = _mm256_setzero_ps(), vs23 = _mm256_setzero_ps();
|
||||
|
||||
for( int k = 0; k < vecsize; k += 8, rptr += 8 )
|
||||
{
|
||||
__m256 w0 = _mm256_load_ps(wptr0 + k);
|
||||
__m256 w1 = _mm256_load_ps(wptr1 + k);
|
||||
__m256 w2 = _mm256_load_ps(wptr2 + k);
|
||||
__m256 r0 = _mm256_load_ps(rptr);
|
||||
|
||||
vs00 = _mm256_fmadd_ps(w0, r0, vs00);
|
||||
vs10 = _mm256_fmadd_ps(w1, r0, vs10);
|
||||
vs20 = _mm256_fmadd_ps(w2, r0, vs20);
|
||||
|
||||
r0 = _mm256_load_ps(rptr + vecsize_aligned);
|
||||
vs01 = _mm256_fmadd_ps(w0, r0, vs01);
|
||||
vs11 = _mm256_fmadd_ps(w1, r0, vs11);
|
||||
vs21 = _mm256_fmadd_ps(w2, r0, vs21);
|
||||
|
||||
r0 = _mm256_load_ps(rptr + vecsize_aligned*2);
|
||||
vs02 = _mm256_fmadd_ps(w0, r0, vs02);
|
||||
vs12 = _mm256_fmadd_ps(w1, r0, vs12);
|
||||
vs22 = _mm256_fmadd_ps(w2, r0, vs22);
|
||||
|
||||
r0 = _mm256_load_ps(rptr + vecsize_aligned*3);
|
||||
vs03 = _mm256_fmadd_ps(w0, r0, vs03);
|
||||
vs13 = _mm256_fmadd_ps(w1, r0, vs13);
|
||||
vs23 = _mm256_fmadd_ps(w2, r0, vs23);
|
||||
}
|
||||
|
||||
__m256 t0 = _mm256_hadd_ps(_mm256_hadd_ps(vs00, vs01), _mm256_hadd_ps(vs02, vs03));
|
||||
__m256 t1 = _mm256_hadd_ps(_mm256_hadd_ps(vs10, vs11), _mm256_hadd_ps(vs12, vs13));
|
||||
__m256 t2 = _mm256_hadd_ps(_mm256_hadd_ps(vs20, vs21), _mm256_hadd_ps(vs22, vs23));
|
||||
|
||||
t0 = _mm256_add_ps(t0, _mm256_permute2f128_ps(t0, t0, 1));
|
||||
t1 = _mm256_add_ps(t1, _mm256_permute2f128_ps(t1, t1, 1));
|
||||
t2 = _mm256_add_ps(t2, _mm256_permute2f128_ps(t2, t2, 1));
|
||||
|
||||
__m256 s0, s1, s2;
|
||||
|
||||
if( initOutput )
|
||||
{
|
||||
s0 = _mm256_set1_ps(bias0);
|
||||
s1 = _mm256_set1_ps(bias1);
|
||||
s2 = _mm256_set1_ps(bias2);
|
||||
}
|
||||
else
|
||||
{
|
||||
s0 = _mm256_castps128_ps256(_mm_loadu_ps(outptr0 + j));
|
||||
s1 = _mm256_castps128_ps256(_mm_loadu_ps(outptr1 + j));
|
||||
s2 = _mm256_castps128_ps256(_mm_loadu_ps(outptr2 + j));
|
||||
}
|
||||
|
||||
s0 = _mm256_add_ps(s0, t0);
|
||||
s1 = _mm256_add_ps(s1, t1);
|
||||
s2 = _mm256_add_ps(s2, t2);
|
||||
|
||||
if( relu )
|
||||
{
|
||||
__m256 m0 = _mm256_cmp_ps(s0, z, _CMP_GT_OS);
|
||||
__m256 m1 = _mm256_cmp_ps(s1, z, _CMP_GT_OS);
|
||||
__m256 m2 = _mm256_cmp_ps(s2, z, _CMP_GT_OS);
|
||||
s0 = _mm256_xor_ps(s0, _mm256_andnot_ps(m0, _mm256_xor_ps(_mm256_mul_ps(s0, vr0), s0)));
|
||||
s1 = _mm256_xor_ps(s1, _mm256_andnot_ps(m1, _mm256_xor_ps(_mm256_mul_ps(s1, vr1), s1)));
|
||||
s2 = _mm256_xor_ps(s2, _mm256_andnot_ps(m2, _mm256_xor_ps(_mm256_mul_ps(s2, vr2), s2)));
|
||||
}
|
||||
|
||||
_mm_storeu_ps(outptr0 + j, _mm256_castps256_ps128(s0));
|
||||
_mm_storeu_ps(outptr1 + j, _mm256_castps256_ps128(s1));
|
||||
_mm_storeu_ps(outptr2 + j, _mm256_castps256_ps128(s2));
|
||||
}
|
||||
|
||||
for( ; j < blockSize; j++ )
|
||||
{
|
||||
const float* rptr = rowbuf + j*vecsize_aligned;
|
||||
float s00, s10, s20;
|
||||
|
||||
if( initOutput )
|
||||
{
|
||||
s00 = bias0;
|
||||
s10 = bias1;
|
||||
s20 = bias2;
|
||||
}
|
||||
else
|
||||
{
|
||||
s00 = outptr0[j];
|
||||
s10 = outptr1[j];
|
||||
s20 = outptr2[j];
|
||||
}
|
||||
|
||||
for( int k = 0; k < vecsize; k++ )
|
||||
{
|
||||
float r0 = rptr[k];
|
||||
s00 += wptr0[k]*r0;
|
||||
s10 += wptr1[k]*r0;
|
||||
s20 += wptr2[k]*r0;
|
||||
}
|
||||
|
||||
if( relu )
|
||||
{
|
||||
s00 = s00 > 0.f ? s00 : s00*r0;
|
||||
s10 = s10 > 0.f ? s10 : s10*r1;
|
||||
s20 = s20 > 0.f ? s20 : s20*r2;
|
||||
}
|
||||
|
||||
outptr0[j] = s00;
|
||||
outptr1[j] = s10;
|
||||
outptr2[j] = s20;
|
||||
}
|
||||
}
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
// dst = vec * weights^t + bias
|
||||
void fastGEMM1T_avx2( const float* vec, const float* weights,
|
||||
size_t wstep, const float* bias,
|
||||
float* dst, int nvecs, int vecsize )
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for( ; i <= nvecs - 8; i += 8 )
|
||||
{
|
||||
const float* wptr = weights + i*wstep;
|
||||
__m256 vs0 = _mm256_setzero_ps(), vs1 = _mm256_setzero_ps(),
|
||||
vs2 = _mm256_setzero_ps(), vs3 = _mm256_setzero_ps(),
|
||||
vs4 = _mm256_setzero_ps(), vs5 = _mm256_setzero_ps(),
|
||||
vs6 = _mm256_setzero_ps(), vs7 = _mm256_setzero_ps();
|
||||
|
||||
for( int k = 0; k < vecsize; k += 8, wptr += 8 )
|
||||
{
|
||||
__m256 v = _mm256_load_ps(vec + k);
|
||||
|
||||
vs0 = _mm256_fmadd_ps(_mm256_load_ps(wptr), v, vs0);
|
||||
vs1 = _mm256_fmadd_ps(_mm256_load_ps(wptr + wstep), v, vs1);
|
||||
vs2 = _mm256_fmadd_ps(_mm256_load_ps(wptr + wstep*2), v, vs2);
|
||||
vs3 = _mm256_fmadd_ps(_mm256_load_ps(wptr + wstep*3), v, vs3);
|
||||
vs4 = _mm256_fmadd_ps(_mm256_load_ps(wptr + wstep*4), v, vs4);
|
||||
vs5 = _mm256_fmadd_ps(_mm256_load_ps(wptr + wstep*5), v, vs5);
|
||||
vs6 = _mm256_fmadd_ps(_mm256_load_ps(wptr + wstep*6), v, vs6);
|
||||
vs7 = _mm256_fmadd_ps(_mm256_load_ps(wptr + wstep*7), v, vs7);
|
||||
}
|
||||
|
||||
__m256 s0 = _mm256_hadd_ps(_mm256_hadd_ps(vs0, vs1), _mm256_hadd_ps(vs2, vs3));
|
||||
__m256 s1 = _mm256_hadd_ps(_mm256_hadd_ps(vs4, vs5), _mm256_hadd_ps(vs6, vs7));
|
||||
|
||||
s0 = _mm256_add_ps(s0, _mm256_permute2f128_ps(s0, s0, 1));
|
||||
s1 = _mm256_add_ps(s1, _mm256_permute2f128_ps(s1, s1, 1));
|
||||
|
||||
s0 = _mm256_add_ps(s0, _mm256_castps128_ps256(_mm_loadu_ps(bias + i)));
|
||||
s1 = _mm256_add_ps(s1, _mm256_castps128_ps256(_mm_loadu_ps(bias + i + 4)));
|
||||
|
||||
_mm_storeu_ps(dst + i, _mm256_castps256_ps128(s0));
|
||||
_mm_storeu_ps(dst + i + 4, _mm256_castps256_ps128(s1));
|
||||
}
|
||||
|
||||
float temp = 0.f;
|
||||
for( ; i < nvecs; i++ )
|
||||
{
|
||||
const float* wptr = weights + i*wstep;
|
||||
__m256 vs0 = _mm256_setzero_ps();
|
||||
|
||||
for( int k = 0; k < vecsize; k += 8, wptr += 8 )
|
||||
{
|
||||
__m256 v = _mm256_load_ps(vec + k);
|
||||
vs0 = _mm256_fmadd_ps(_mm256_load_ps(wptr), v, vs0);
|
||||
}
|
||||
|
||||
__m256 s0 = _mm256_hadd_ps(_mm256_hadd_ps(vs0, vs0), vs0);
|
||||
s0 = _mm256_add_ps(s0, _mm256_permute2f128_ps(s0, s0, 1));
|
||||
_mm_store_ss(&temp, _mm256_castps256_ps128(s0));
|
||||
dst[i] = temp + bias[i];
|
||||
}
|
||||
|
||||
_mm256_zeroupper();
|
||||
}
|
||||
|
||||
void fastGEMM_avx2( const float* aptr, size_t astep, const float* bptr,
|
||||
size_t bstep, float* cptr, size_t cstep,
|
||||
int ma, int na, int nb )
|
||||
{
|
||||
int n = 0;
|
||||
for( ; n <= nb - 16; n += 16 )
|
||||
{
|
||||
for( int m = 0; m < ma; m += 4 )
|
||||
{
|
||||
const float* aptr0 = aptr + astep*m;
|
||||
const float* aptr1 = aptr + astep*std::min(m+1, ma-1);
|
||||
const float* aptr2 = aptr + astep*std::min(m+2, ma-1);
|
||||
const float* aptr3 = aptr + astep*std::min(m+3, ma-1);
|
||||
|
||||
float* cptr0 = cptr + cstep*m;
|
||||
float* cptr1 = cptr + cstep*std::min(m+1, ma-1);
|
||||
float* cptr2 = cptr + cstep*std::min(m+2, ma-1);
|
||||
float* cptr3 = cptr + cstep*std::min(m+3, ma-1);
|
||||
|
||||
__m256 d00 = _mm256_setzero_ps(), d01 = _mm256_setzero_ps();
|
||||
__m256 d10 = _mm256_setzero_ps(), d11 = _mm256_setzero_ps();
|
||||
__m256 d20 = _mm256_setzero_ps(), d21 = _mm256_setzero_ps();
|
||||
__m256 d30 = _mm256_setzero_ps(), d31 = _mm256_setzero_ps();
|
||||
|
||||
for( int k = 0; k < na; k++ )
|
||||
{
|
||||
__m256 a0 = _mm256_set1_ps(aptr0[k]);
|
||||
__m256 a1 = _mm256_set1_ps(aptr1[k]);
|
||||
__m256 a2 = _mm256_set1_ps(aptr2[k]);
|
||||
__m256 a3 = _mm256_set1_ps(aptr3[k]);
|
||||
__m256 b0 = _mm256_loadu_ps(bptr + k*bstep + n);
|
||||
__m256 b1 = _mm256_loadu_ps(bptr + k*bstep + n + 8);
|
||||
d00 = _mm256_fmadd_ps(a0, b0, d00);
|
||||
d01 = _mm256_fmadd_ps(a0, b1, d01);
|
||||
d10 = _mm256_fmadd_ps(a1, b0, d10);
|
||||
d11 = _mm256_fmadd_ps(a1, b1, d11);
|
||||
d20 = _mm256_fmadd_ps(a2, b0, d20);
|
||||
d21 = _mm256_fmadd_ps(a2, b1, d21);
|
||||
d30 = _mm256_fmadd_ps(a3, b0, d30);
|
||||
d31 = _mm256_fmadd_ps(a3, b1, d31);
|
||||
}
|
||||
|
||||
_mm256_storeu_ps(cptr0 + n, d00);
|
||||
_mm256_storeu_ps(cptr0 + n + 8, d01);
|
||||
_mm256_storeu_ps(cptr1 + n, d10);
|
||||
_mm256_storeu_ps(cptr1 + n + 8, d11);
|
||||
_mm256_storeu_ps(cptr2 + n, d20);
|
||||
_mm256_storeu_ps(cptr2 + n + 8, d21);
|
||||
_mm256_storeu_ps(cptr3 + n, d30);
|
||||
_mm256_storeu_ps(cptr3 + n + 8, d31);
|
||||
}
|
||||
}
|
||||
_mm256_zeroupper();
|
||||
|
||||
for( ; n < nb; n++ )
|
||||
{
|
||||
for( int m = 0; m < ma; m++ )
|
||||
{
|
||||
const float* aptr0 = aptr + astep*m;
|
||||
float* cptr0 = cptr + cstep*m;
|
||||
float d0 = 0.f;
|
||||
|
||||
for( int k = 0; k < na; k++ )
|
||||
d0 += aptr0[k]*bptr[k*bstep + n];
|
||||
|
||||
cptr0[n] = d0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,205 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "layers_common.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
namespace util
|
||||
{
|
||||
|
||||
std::string makeName(const std::string& str1, const std::string& str2)
|
||||
{
|
||||
return str1 + str2;
|
||||
}
|
||||
|
||||
bool getParameter(const LayerParams ¶ms, const std::string& nameBase, const std::string& nameAll,
|
||||
int ¶meterH, int ¶meterW, bool hasDefault = false, const int& defaultValue = 0)
|
||||
{
|
||||
std::string nameH = makeName(nameBase, std::string("_h"));
|
||||
std::string nameW = makeName(nameBase, std::string("_w"));
|
||||
std::string nameAll_ = nameAll;
|
||||
if(nameAll_ == "")
|
||||
{
|
||||
nameAll_ = nameBase;
|
||||
}
|
||||
|
||||
if (params.has(nameH) && params.has(nameW))
|
||||
{
|
||||
parameterH = params.get<int>(nameH);
|
||||
parameterW = params.get<int>(nameW);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (params.has(nameAll_))
|
||||
{
|
||||
parameterH = parameterW = params.get<int>(nameAll_);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(hasDefault)
|
||||
{
|
||||
parameterH = parameterW = defaultValue;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getKernelSize(const LayerParams ¶ms, int &kernelH, int &kernelW)
|
||||
{
|
||||
if(!util::getParameter(params, "kernel", "kernel_size", kernelH, kernelW))
|
||||
{
|
||||
CV_Error(cv::Error::StsBadArg, "kernel_size (or kernel_h and kernel_w) not specified");
|
||||
}
|
||||
|
||||
CV_Assert(kernelH > 0 && kernelW > 0);
|
||||
}
|
||||
|
||||
void getStrideAndPadding(const LayerParams ¶ms, int &padH, int &padW, int &strideH, int &strideW, cv::String& padMode)
|
||||
{
|
||||
util::getParameter(params, "pad", "pad", padH, padW, true, 0);
|
||||
util::getParameter(params, "stride", "stride", strideH, strideW, true, 1);
|
||||
|
||||
padMode = "";
|
||||
if (params.has("pad_mode"))
|
||||
{
|
||||
padMode = params.get<String>("pad_mode");
|
||||
}
|
||||
|
||||
CV_Assert(padH >= 0 && padW >= 0 && strideH > 0 && strideW > 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void getPoolingKernelParams(const LayerParams ¶ms, int &kernelH, int &kernelW, bool &globalPooling,
|
||||
int &padH, int &padW, int &strideH, int &strideW, cv::String &padMode)
|
||||
{
|
||||
util::getStrideAndPadding(params, padH, padW, strideH, strideW, padMode);
|
||||
|
||||
globalPooling = params.has("global_pooling");
|
||||
|
||||
if (globalPooling)
|
||||
{
|
||||
if(params.has("kernel_h") || params.has("kernel_w") || params.has("kernel_size"))
|
||||
{
|
||||
CV_Error(cv::Error::StsBadArg, "In global_pooling mode, kernel_size (or kernel_h and kernel_w) cannot be specified");
|
||||
}
|
||||
if(padH != 0 || padW != 0 || strideH != 1 || strideW != 1)
|
||||
{
|
||||
CV_Error(cv::Error::StsBadArg, "In global_pooling mode, pad_h and pad_w must be = 0, and stride_h and stride_w must be = 1");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
util::getKernelSize(params, kernelH, kernelW);
|
||||
}
|
||||
}
|
||||
|
||||
void getConvolutionKernelParams(const LayerParams ¶ms, int &kernelH, int &kernelW, int &padH, int &padW,
|
||||
int &strideH, int &strideW, int &dilationH, int &dilationW, cv::String &padMode)
|
||||
{
|
||||
util::getKernelSize(params, kernelH, kernelW);
|
||||
util::getStrideAndPadding(params, padH, padW, strideH, strideW, padMode);
|
||||
|
||||
util::getParameter(params, "dilation", "dilation", dilationH, dilationW, true, 1);
|
||||
|
||||
CV_Assert(dilationH > 0 && dilationW > 0);
|
||||
}
|
||||
|
||||
// From TensorFlow code:
|
||||
// Total padding on rows and cols is
|
||||
// Pr = (R' - 1) * S + Kr - R
|
||||
// Pc = (C' - 1) * S + Kc - C
|
||||
// where (R', C') are output dimensions, (R, C) are input dimensions, S
|
||||
// is stride, (Kr, Kc) are filter dimensions.
|
||||
// We pad Pr/2 on the left and Pr - Pr/2 on the right, Pc/2 on the top
|
||||
// and Pc - Pc/2 on the bottom. When Pr or Pc is odd, this means
|
||||
// we pad more on the right and bottom than on the top and left.
|
||||
void getConvPoolOutParams(const Size& inp, const Size &kernel,
|
||||
const Size &stride, const String &padMode,
|
||||
Size& out)
|
||||
{
|
||||
if (padMode == "VALID")
|
||||
{
|
||||
out.height = (inp.height - kernel.height + stride.height) / stride.height;
|
||||
out.width = (inp.width- kernel.width + stride.width) / stride.width;
|
||||
}
|
||||
else if (padMode == "SAME")
|
||||
{
|
||||
out.height = (inp.height - 1 + stride.height) / stride.height;
|
||||
out.width = (inp.width - 1 + stride.width) / stride.width;
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::StsError, "Unsupported padding mode");
|
||||
}
|
||||
}
|
||||
|
||||
void getConvPoolPaddings(const Size& inp, const Size& out,
|
||||
const Size &kernel, const Size &stride,
|
||||
const String &padMode, Size &pad)
|
||||
{
|
||||
if (padMode == "VALID")
|
||||
{
|
||||
pad = cv::Size(0,0);
|
||||
}
|
||||
else if (padMode == "SAME")
|
||||
{
|
||||
int Ph = std::max(0, (out.height - 1) * stride.height + kernel.height - inp.height);
|
||||
int Pw = std::max(0, (out.width - 1) * stride.width + kernel.width - inp.width);
|
||||
// For odd values of total padding, add more padding at the 'right'
|
||||
// side of the given dimension.
|
||||
pad = cv::Size(Pw / 2, Ph / 2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#ifndef __OPENCV_DNN_LAYERS_LAYERS_COMMON_HPP__
|
||||
#define __OPENCV_DNN_LAYERS_LAYERS_COMMON_HPP__
|
||||
#include <opencv2/dnn.hpp>
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
void getConvolutionKernelParams(const LayerParams ¶ms, int &kernelH, int &kernelW, int &padH, int &padW,
|
||||
int &strideH, int &strideW, int &dilationH, int &dilationW, cv::String& padMode);
|
||||
|
||||
void getPoolingKernelParams(const LayerParams ¶ms, int &kernelH, int &kernelW, bool &globalPooling,
|
||||
int &padH, int &padW, int &strideH, int &strideW, cv::String& padMode);
|
||||
|
||||
void getConvPoolOutParams(const Size& inp, const Size &kernel,
|
||||
const Size &stride, const String &padMode,
|
||||
Size& out);
|
||||
|
||||
void getConvPoolPaddings(const Size& inp, const Size& out,
|
||||
const Size &kernel, const Size &stride,
|
||||
const String &padMode, Size &pad);
|
||||
|
||||
#if CV_SSE2
|
||||
#define CV_DNN_TRY_AVX2 1
|
||||
|
||||
void fastConv_avx2(const float* weights, size_t wstep, const float* bias,
|
||||
const float* rowbuf, float* output, const int* outShape,
|
||||
int blockSize, int vecsize, int vecsize_aligned,
|
||||
const float* relu, bool initOutput);
|
||||
void fastGEMM1T_avx2( const float* vec, const float* weights,
|
||||
size_t wstep, const float* bias,
|
||||
float* dst, int nvecs, int vecsize );
|
||||
void fastGEMM_avx2( const float* aptr, size_t astep, const float* bptr0,
|
||||
size_t bstep, float* cptr, size_t cstep,
|
||||
int ma, int na, int nb );
|
||||
|
||||
#else
|
||||
#define CV_DNN_TRY_AVX2 0
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,341 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "op_halide.hpp"
|
||||
#include "opencv2/imgproc.hpp"
|
||||
#include "opencv2/dnn/shape_utils.hpp"
|
||||
#include "opencv2/core/hal/hal.hpp"
|
||||
#include <algorithm>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class LRNLayerImpl : public LRNLayer
|
||||
{
|
||||
public:
|
||||
LRNLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
type = -1;
|
||||
String nrmType = params.get<String>("norm_region", "ACROSS_CHANNELS");
|
||||
if (nrmType == "ACROSS_CHANNELS")
|
||||
type = LRNLayer::CHANNEL_NRM;
|
||||
else if (nrmType == "WITHIN_CHANNEL")
|
||||
type = LRNLayer::SPATIAL_NRM;
|
||||
else
|
||||
CV_Error(Error::StsBadArg, "Unknown region type \"" + nrmType + "\"");
|
||||
|
||||
size = params.get<int>("local_size", 5);
|
||||
if (size % 2 != 1 || size <= 0)
|
||||
CV_Error(Error::StsBadArg, "LRN layer supports only positive odd values for local_size");
|
||||
|
||||
alpha = params.get<double>("alpha", 1);
|
||||
beta = params.get<double>("beta", 0.75);
|
||||
bias = params.get<double>("bias", 1);
|
||||
normBySize = params.get<bool>("norm_by_size", true);
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide();
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
CV_Assert(inputs.size() == outputs.size());
|
||||
for (int i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
CV_Assert(inputs[i]->dims == 4);
|
||||
|
||||
Mat &src = *inputs[i];
|
||||
Mat &dst = outputs[i];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case CHANNEL_NRM:
|
||||
channelNormalization(src, dst);
|
||||
break;
|
||||
case SPATIAL_NRM:
|
||||
spatialNormalization(src, dst);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsNotImplemented, "Unimplemented mode of LRN layer");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ChannelLRN : public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
ChannelLRN(const float* src, float* dst, int channels, int ksize,
|
||||
float alpha1, float bias1, float beta1,
|
||||
size_t planeSize, int nsamples, int nstripes)
|
||||
{
|
||||
src_ = src; dst_ = dst;
|
||||
channels_ = channels;
|
||||
ksize_ = ksize;
|
||||
alpha1_ = alpha1; bias1_ = bias1; beta1_ = beta1;
|
||||
planeSize_ = planeSize; nsamples_ = nsamples; nstripes_ = nstripes;
|
||||
}
|
||||
|
||||
void operator()(const Range& r) const
|
||||
{
|
||||
int nsamples = nsamples_, nstripes = nstripes_;
|
||||
size_t planeSize = planeSize_, planeSize_n = planeSize * nsamples;
|
||||
size_t elemsPerStripe = (planeSize_n + nstripes - 1)/nstripes;
|
||||
size_t rstart = r.start*elemsPerStripe;
|
||||
size_t rend = r.end == nstripes ? planeSize_n : r.end*elemsPerStripe;
|
||||
rstart = std::min(rstart, planeSize_n);
|
||||
rend = std::min(rend, planeSize_n);
|
||||
float alpha1 = alpha1_, bias1 = bias1_, beta1 = beta1_;
|
||||
int k, channels = channels_, ksize = ksize_;
|
||||
|
||||
AutoBuffer<float> buf_((channels + ksize*2 + 4)*2);
|
||||
float* acc = (float*)buf_;
|
||||
float* buf = acc + channels + ksize + 1;
|
||||
for( k = 0; k <= ksize; k++ )
|
||||
buf[-k-1] = buf[channels + k] = 0.f;
|
||||
|
||||
for( size_t ofs = rstart; ofs < rend; )
|
||||
{
|
||||
int sampleIdx = (int)(ofs/planeSize);
|
||||
if( sampleIdx >= nsamples )
|
||||
break;
|
||||
size_t ofs0 = ofs - sampleIdx*planeSize;
|
||||
size_t ofs1 = std::min(planeSize - ofs0, rend - ofs) + ofs;
|
||||
const float* src = src_ + sampleIdx*planeSize*channels + ofs0;
|
||||
float* dst = dst_ + sampleIdx*planeSize*channels + ofs0;
|
||||
|
||||
for( ; ofs < ofs1; ofs++, src++, dst++ )
|
||||
{
|
||||
for( k = 0; k < channels; k++ )
|
||||
buf[k] = src[k*planeSize];
|
||||
float s = 0;
|
||||
for( k = 0; k < ksize; k++ )
|
||||
s += buf[k]*buf[k];
|
||||
for( k = 0; k < channels; k++ )
|
||||
{
|
||||
float x1 = buf[k + ksize];
|
||||
float x0 = buf[k - ksize - 1];
|
||||
s = std::max(s + (x1 + x0)*(x1 - x0), 0.f);
|
||||
acc[k] = (float)(alpha1*s + bias1);
|
||||
}
|
||||
|
||||
hal::log32f(acc, acc, channels);
|
||||
for( k = 0; k < channels; k++ )
|
||||
acc[k] *= beta1;
|
||||
hal::exp32f(acc, acc, channels);
|
||||
|
||||
for( k = 0; k < channels; k++ )
|
||||
dst[k*planeSize] = buf[k]*acc[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const float* src_;
|
||||
float* dst_;
|
||||
float alpha1_, bias1_, beta1_;
|
||||
size_t planeSize_;
|
||||
int channels_, ksize_, nsamples_, nstripes_;
|
||||
};
|
||||
|
||||
void channelNormalization(Mat &srcBlob, Mat &dstBlob)
|
||||
{
|
||||
int num = srcBlob.size[0];
|
||||
int channels = srcBlob.size[1];
|
||||
int ksize = (size - 1) / 2;
|
||||
int sizeNormFactor = normBySize ? size : 1;
|
||||
size_t planeSize = srcBlob.size[2]*srcBlob.size[3];
|
||||
|
||||
int nstripes = std::max(getNumThreads(), 1);
|
||||
|
||||
ChannelLRN clrn(srcBlob.ptr<float>(), dstBlob.ptr<float>(), channels,
|
||||
ksize, alpha/sizeNormFactor, bias, -beta, planeSize, num, nstripes);
|
||||
parallel_for_(Range(0, nstripes), clrn, nstripes);
|
||||
}
|
||||
|
||||
void sqrBoxFilter_(const Mat &src, Mat &dst)
|
||||
{
|
||||
Mat srcRawWrapper(src.rows, src.cols, src.type(), src.data, src.step[0]);
|
||||
cv::sqrBoxFilter(srcRawWrapper, dst, dst.depth(), Size(size, size), Point(-1, -1), false, BORDER_CONSTANT);
|
||||
}
|
||||
|
||||
void spatialNormalization(Mat &srcBlob, Mat &dstBlob)
|
||||
{
|
||||
int num = srcBlob.size[0];
|
||||
int channels = srcBlob.size[1];
|
||||
int sizeNormFactor = normBySize ? size*size : 1;
|
||||
|
||||
Mat srcMat = srcBlob;
|
||||
Mat dstMat = dstBlob;
|
||||
|
||||
for (int n = 0; n < num; n++)
|
||||
{
|
||||
for (int cn = 0; cn < channels; cn++)
|
||||
{
|
||||
Mat src = getPlane(srcMat, n, cn);
|
||||
Mat dst = getPlane(dstMat, n, cn);
|
||||
|
||||
sqrBoxFilter_(src, dst);
|
||||
|
||||
dst.convertTo(dst, dst.type(), alpha/sizeNormFactor, bias);
|
||||
cv::pow(dst, beta, dst);
|
||||
cv::divide(src, dst, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
float alphaSize = alpha;
|
||||
if (normBySize)
|
||||
alphaSize /= (type == CHANNEL_NRM ? size : size * size);
|
||||
int width, height, channels, numImgs;
|
||||
Halide::Buffer<float> inputBuffer = halideBuffer(inputs[0]);
|
||||
getCanonicalSize(inputBuffer, &width, &height, &channels, &numImgs);
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::Func padded_sq(name + "_padded_sq");
|
||||
Halide::Func sq("sq");
|
||||
sq(x, y, c, n) = inputBuffer(x, y, c, n) * inputBuffer(x, y, c, n);
|
||||
|
||||
Halide::Func bounded =
|
||||
Halide::BoundaryConditions::constant_exterior(sq, 0, 0, width,
|
||||
0, height,
|
||||
0, channels,
|
||||
0, numImgs);
|
||||
padded_sq(x, y, c, n) = bounded(x, y, c, n);
|
||||
|
||||
Halide::Expr base;
|
||||
if (type == CHANNEL_NRM)
|
||||
{
|
||||
Halide::RDom r((1 - size) / 2, size);
|
||||
base = alphaSize * sum(padded_sq(x, y, c + r, n));
|
||||
}
|
||||
else // SPATIAL_NRM
|
||||
{
|
||||
Halide::RDom r((1 - size) / 2, size, (1 - size) / 2, size);
|
||||
base = alphaSize * sum(padded_sq(x + r.x, y + r.y, c, n));
|
||||
}
|
||||
base += static_cast<float>(bias);
|
||||
top(x, y, c, n) = inputBuffer(x, y, c, n) / pow(base, beta);
|
||||
return Ptr<BackendNode>(new HalideBackendNode({ padded_sq, top }));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
virtual void applyHalideScheduler(Ptr<BackendNode>& node,
|
||||
const std::vector<Mat*> &inputs,
|
||||
const std::vector<Mat> &outputs,
|
||||
int targetId) const
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
if (targetId != DNN_TARGET_CPU)
|
||||
{
|
||||
Layer::applyHalideScheduler(node, inputs, outputs, targetId);
|
||||
return;
|
||||
}
|
||||
int outW, outH, outC, outN;
|
||||
getCanonicalSize(outputs[0].size, &outW, &outH, &outC, &outN);
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n"), yo("yo"), yi("yi"), tile("tile");
|
||||
Halide::Func& top = node.dynamicCast<HalideBackendNode>()->funcs[1];
|
||||
Halide::Func& padded_sq = node.dynamicCast<HalideBackendNode>()->funcs[0];
|
||||
|
||||
if (outW < 8 || outH <= 2)
|
||||
return;
|
||||
|
||||
top.reorder(x, c, y, n)
|
||||
.split(y, yo, yi, 2)
|
||||
.fuse(yo, n, tile)
|
||||
.parallel(tile)
|
||||
.unroll(yi)
|
||||
.vectorize(x, 8);
|
||||
padded_sq.store_at(top, tile)
|
||||
.compute_at(top, yi);
|
||||
#endif // HAVE_HALIDE
|
||||
}
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)outputs; // suppress unused variable warning
|
||||
CV_Assert(inputs.size() > 0);
|
||||
long flops = 0;
|
||||
|
||||
for(int i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
if (type == CHANNEL_NRM)
|
||||
{
|
||||
int channels = inputs[i][1];
|
||||
int ksize = (size - 1) / 2;
|
||||
|
||||
flops += inputs[i][0]*(std::min(ksize, channels)*2*total(inputs[i], 2) + channels*4*total(inputs[i], 2));
|
||||
|
||||
if (ksize < channels)
|
||||
{
|
||||
flops += (size + 2*(channels - size))*total(inputs[i], 2);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
flops += total(inputs[i])*(2*size*size + 2);
|
||||
}
|
||||
}
|
||||
return flops;
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<LRNLayer> LRNLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<LRNLayer>(new LRNLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2016, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
/*
|
||||
Implementation of Batch Normalization layer.
|
||||
*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "op_halide.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class MaxUnpoolLayerImpl : public MaxUnpoolLayer
|
||||
{
|
||||
public:
|
||||
MaxUnpoolLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
poolKernel = Size(params.get<int>("pool_k_w"), params.get<int>("pool_k_h"));
|
||||
poolPad = Size(params.get<int>("pool_pad_w"), params.get<int>("pool_pad_h"));
|
||||
poolStride = Size(params.get<int>("pool_stride_w"), params.get<int>("pool_stride_h"));
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide() &&
|
||||
!poolPad.width && !poolPad.height;
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() == 2);
|
||||
CV_Assert(total(inputs[0]) == total(inputs[1]));
|
||||
|
||||
MatShape outShape = inputs[0];
|
||||
outShape[2] = (outShape[2] - 1) * poolStride.height + poolKernel.height - 2 * poolPad.height;
|
||||
outShape[3] = (outShape[3] - 1) * poolStride.width + poolKernel.width - 2 * poolPad.width;
|
||||
|
||||
outputs.clear();
|
||||
outputs.push_back(outShape);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
CV_Assert(inputs.size() == 2);
|
||||
Mat& input = *inputs[0];
|
||||
Mat& indices = *inputs[1];
|
||||
|
||||
CV_Assert(input.total() == indices.total());
|
||||
CV_Assert(input.size[0] == 1);
|
||||
CV_Assert(input.isContinuous());
|
||||
|
||||
for(int i_n = 0; i_n < outputs.size(); i_n++)
|
||||
{
|
||||
Mat& outBlob = outputs[i_n];
|
||||
outBlob.setTo(0);
|
||||
CV_Assert(input.size[1] == outBlob.size[1]);
|
||||
int outPlaneTotal = outBlob.size[2]*outBlob.size[3];
|
||||
|
||||
for (int i_c = 0; i_c < input.size[1]; i_c++)
|
||||
{
|
||||
Mat outPlane = getPlane(outBlob, 0, i_c);
|
||||
int wh_area = input.size[2]*input.size[3];
|
||||
const float* inptr = input.ptr<float>(0, i_c);
|
||||
const float* idxptr = indices.ptr<float>(0, i_c);
|
||||
float* outptr = outPlane.ptr<float>();
|
||||
|
||||
for(int i_wh = 0; i_wh < wh_area; i_wh++)
|
||||
{
|
||||
int index = idxptr[i_wh];
|
||||
CV_Assert(0 <= index && index < outPlaneTotal);
|
||||
outptr[index] = inptr[i_wh];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &input)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
// Meaningless operation if false because if kernel > stride
|
||||
// it is not deterministic and if kernel < stride we just
|
||||
// skip a part of input data (you'd better change your model).
|
||||
if (poolKernel.width != poolStride.width ||
|
||||
poolKernel.height != poolStride.height)
|
||||
CV_Error(cv::Error::StsNotImplemented,
|
||||
"Halide backend for maximum unpooling "
|
||||
"is not support cases when kernel != stride");
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::Buffer<float> inputBuffer = halideBuffer(input[0]);
|
||||
Halide::Buffer<float> indices = halideBuffer(input[1]);
|
||||
|
||||
Halide::Expr pooledX = x / poolKernel.width;
|
||||
Halide::Expr pooledY = y / poolKernel.height;
|
||||
|
||||
const int outW = inputBuffer.width() * poolKernel.width;
|
||||
top(x, y, c, n) = select(y * outW + x == indices(pooledX, pooledY, c, n),
|
||||
inputBuffer(pooledX, pooledY, c, n), 0.0f);
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<MaxUnpoolLayer> MaxUnpoolLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<MaxUnpoolLayer>(new MaxUnpoolLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class MVNLayerImpl : public MVNLayer
|
||||
{
|
||||
public:
|
||||
MVNLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
normVariance = params.get<bool>("normalize_variance", true);
|
||||
acrossChannels = params.get<bool>("across_channels", false);
|
||||
eps = params.get<double>("eps", 1e-9);
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat *> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
for (size_t inpIdx = 0; inpIdx < inputs.size(); inpIdx++)
|
||||
{
|
||||
Mat &inpBlob = *inputs[inpIdx];
|
||||
Mat &outBlob = outputs[inpIdx];
|
||||
|
||||
int splitDim = (acrossChannels) ? 1 : 2;
|
||||
int i, newRows = 1;
|
||||
for( i = 0; i < splitDim; i++ )
|
||||
newRows *= inpBlob.size[i];
|
||||
Mat inpMat = inpBlob.reshape(1, newRows);
|
||||
Mat outMat = outBlob.reshape(1, newRows);
|
||||
|
||||
Scalar mean, dev;
|
||||
for ( i = 0; i < newRows; i++)
|
||||
{
|
||||
Mat inpRow = inpMat.row(i);
|
||||
Mat outRow = outMat.row(i);
|
||||
|
||||
cv::meanStdDev(inpRow, mean, (normVariance) ? dev : noArray());
|
||||
double alpha = (normVariance) ? 1/(eps + dev[0]) : 1;
|
||||
inpRow.convertTo(outRow, outRow.type(), alpha, -mean[0] * alpha);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)outputs; // suppress unused variable warning
|
||||
long flops = 0;
|
||||
for(int i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
flops += 6*total(inputs[i]) + 3*total(inputs[i], 0, normVariance ? 2 : 1);
|
||||
}
|
||||
return flops;
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<MVNLayer> MVNLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<MVNLayer>(new MVNLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
|
||||
#include <float.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
const std::string layerName = "NormalizeBBox";
|
||||
}
|
||||
|
||||
class NormalizeBBoxLayerImpl : public NormalizeBBoxLayer
|
||||
{
|
||||
float _eps;
|
||||
bool _across_spatial;
|
||||
bool _channel_shared;
|
||||
public:
|
||||
bool getParameterDict(const LayerParams ¶ms,
|
||||
const std::string ¶meterName,
|
||||
DictValue& result)
|
||||
{
|
||||
if (!params.has(parameterName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = params.get(parameterName);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T getParameter(const LayerParams ¶ms,
|
||||
const std::string ¶meterName,
|
||||
const size_t &idx=0,
|
||||
const bool required=true,
|
||||
const T& defaultValue=T())
|
||||
{
|
||||
DictValue dictValue;
|
||||
bool success = getParameterDict(params, parameterName, dictValue);
|
||||
if(!success)
|
||||
{
|
||||
if(required)
|
||||
{
|
||||
std::string message = layerName;
|
||||
message += " layer parameter does not contain ";
|
||||
message += parameterName;
|
||||
message += " parameter.";
|
||||
CV_Error(Error::StsBadArg, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return dictValue.get<T>(idx);
|
||||
}
|
||||
|
||||
NormalizeBBoxLayerImpl(const LayerParams ¶ms)
|
||||
{
|
||||
_eps = getParameter<float>(params, "eps", 0, false, 1e-10f);
|
||||
_across_spatial = getParameter<bool>(params, "across_spatial");
|
||||
_channel_shared = getParameter<bool>(params, "channel_shared");
|
||||
setParamsFrom(params);
|
||||
}
|
||||
|
||||
void checkInputs(const std::vector<Mat*> &inputs)
|
||||
{
|
||||
CV_Assert(inputs.size() > 0);
|
||||
CV_Assert(inputs[0]->dims == 4 && inputs[0]->type() == CV_32F);
|
||||
for (size_t i = 1; i < inputs.size(); i++)
|
||||
{
|
||||
CV_Assert(inputs[i]->dims == 4 && inputs[i]->type() == CV_32F);
|
||||
CV_Assert(inputs[i]->size == inputs[0]->size);
|
||||
}
|
||||
CV_Assert(inputs[0]->dims > 2);
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
bool inplace = Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
|
||||
size_t channels = inputs[0][1];
|
||||
size_t rows = inputs[0][2];
|
||||
size_t cols = inputs[0][3];
|
||||
size_t channelSize = rows * cols;
|
||||
|
||||
internals.assign(1, shape(channels, channelSize));
|
||||
internals.push_back(shape(channels, 1));
|
||||
internals.push_back(shape(1, channelSize));
|
||||
|
||||
return inplace;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
checkInputs(inputs);
|
||||
|
||||
Mat& buffer = internals[0], sumChannelMultiplier = internals[1],
|
||||
sumSpatialMultiplier = internals[2];
|
||||
|
||||
sumChannelMultiplier.setTo(1.0);
|
||||
sumSpatialMultiplier.setTo(1.0);
|
||||
|
||||
const Mat& inp0 = *inputs[0];
|
||||
size_t num = inp0.size[0];
|
||||
size_t channels = inp0.size[1];
|
||||
size_t channelSize = inp0.size[2] * inp0.size[3];
|
||||
|
||||
Mat zeroBuffer(channels, channelSize, CV_32F, Scalar(0));
|
||||
Mat absDiff;
|
||||
Mat scale = blobs[0];
|
||||
for (size_t j = 0; j < inputs.size(); j++)
|
||||
{
|
||||
for (size_t n = 0; n < num; ++n)
|
||||
{
|
||||
Mat src = Mat(channels, channelSize, CV_32F, inputs[j]->ptr<float>(n));
|
||||
Mat dst = Mat(channels, channelSize, CV_32F, outputs[j].ptr<float>(n));
|
||||
|
||||
buffer = src.mul(src);
|
||||
|
||||
if (_across_spatial)
|
||||
{
|
||||
absdiff(buffer, zeroBuffer, absDiff);
|
||||
|
||||
// add eps to avoid overflow
|
||||
double absSum = sum(absDiff)[0] + _eps;
|
||||
|
||||
float norm = sqrt(absSum);
|
||||
dst = src / norm;
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat norm(channelSize, 1, buffer.type()); // 1 x channelSize
|
||||
|
||||
// (_channels x channelSize)T * _channels x 1 -> channelSize x 1
|
||||
gemm(buffer, sumChannelMultiplier, 1, norm, 0, norm, GEMM_1_T);
|
||||
|
||||
// compute norm
|
||||
pow(norm, 0.5f, norm);
|
||||
|
||||
// scale the layer
|
||||
// _channels x 1 * (channelSize x 1)T -> _channels x channelSize
|
||||
gemm(sumChannelMultiplier, norm, 1, buffer, 0, buffer, GEMM_2_T);
|
||||
|
||||
dst = src / buffer;
|
||||
}
|
||||
|
||||
// scale the output
|
||||
if (_channel_shared)
|
||||
{
|
||||
// _scale: 1 x 1
|
||||
dst *= scale.at<float>(0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// _scale: _channels x 1
|
||||
// _channels x 1 * 1 x channelSize -> _channels x channelSize
|
||||
gemm(scale, sumSpatialMultiplier, 1, buffer, 0, buffer);
|
||||
|
||||
dst = dst.mul(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
Ptr<NormalizeBBoxLayer> NormalizeBBoxLayer::create(const LayerParams ¶ms)
|
||||
{
|
||||
return Ptr<NormalizeBBoxLayer>(new NormalizeBBoxLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2016, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
/*
|
||||
Implementation of padding layer, which adds paddings to input blob.
|
||||
*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "op_halide.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class PaddingLayerImpl : public PaddingLayer
|
||||
{
|
||||
public:
|
||||
PaddingLayerImpl(const LayerParams ¶ms)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
paddingDim = params.get<int>("padding_dim");
|
||||
padding = abs(params.get<int>("padding"));
|
||||
inputDims = params.get<int>("input_dims", 0);
|
||||
index = params.get<int>("index", 0);
|
||||
paddingValue = params.get<double>("value", 0);
|
||||
|
||||
if(paddingDim < 0 || padding < 0)
|
||||
CV_Error(cv::Error::StsNotImplemented, "Negative padding and dim aren't supported");
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
outputs.clear();
|
||||
for(int i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
MatShape shape = inputs[i];
|
||||
int dim = getPadDim(shape);
|
||||
CV_Assert(dim < shape.size());
|
||||
|
||||
shape[dim] += padding;
|
||||
outputs.push_back(shape);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide();
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
for(int i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
outputs[i] = paddingValue;
|
||||
const Mat& inp = *inputs[i];
|
||||
Mat& out = outputs[i];
|
||||
int dims = inp.dims;
|
||||
MatShape inShape(inp.size.p, inp.size.p + dims);
|
||||
MatShape outShape(out.size.p, out.size.p + dims);
|
||||
int dim = getPadDim(inShape);
|
||||
|
||||
int actualIndex = index;
|
||||
if(index == 0)
|
||||
actualIndex = inShape[dim];
|
||||
|
||||
std::vector<std::pair<Range, Range> > srcDstRanges;
|
||||
srcDstRanges.push_back(std::make_pair(Range(0, actualIndex), Range(0, actualIndex)));
|
||||
srcDstRanges.push_back(std::make_pair(Range(actualIndex, inShape[dim]),
|
||||
Range(actualIndex + padding, outShape[dim])));
|
||||
|
||||
std::vector<Range> srcRanges(dims, Range::all()), dstRanges = srcRanges;
|
||||
|
||||
for(int j = 0; j < srcDstRanges.size(); j++)
|
||||
{
|
||||
if(!srcDstRanges[j].first.empty())
|
||||
{
|
||||
srcRanges[dim] = srcDstRanges[j].first;
|
||||
dstRanges[dim] = srcDstRanges[j].second;
|
||||
Mat dst = out(&dstRanges[0]);
|
||||
Mat src = inp(&srcRanges[0]).clone();
|
||||
src.copyTo(dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int getPadDim(const MatShape& shape) const
|
||||
{
|
||||
return inputDims > 0 && (int)shape.size() > inputDims ? paddingDim + 1 : paddingDim;
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
int inW, inH, inC, inN;
|
||||
Halide::Buffer<float> inputBuffer = halideBuffer(inputs[0]);
|
||||
getCanonicalSize(inputBuffer, &inW, &inH, &inC, &inN);
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::Func padded =
|
||||
Halide::BoundaryConditions::constant_exterior(inputBuffer, paddingValue);
|
||||
top(x, y, c, n) = padded(x, y, c, n);
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
int paddingDim, padding, inputDims, index;
|
||||
float paddingValue;
|
||||
};
|
||||
|
||||
Ptr<PaddingLayer> PaddingLayer::create(const LayerParams ¶ms)
|
||||
{
|
||||
return Ptr<PaddingLayer>(new PaddingLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,243 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include <float.h>
|
||||
#include <algorithm>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
class PermuteLayerImpl : public PermuteLayer
|
||||
{
|
||||
public:
|
||||
void checkCurrentOrder(int currentOrder)
|
||||
{
|
||||
if(currentOrder < 0 || currentOrder > 3)
|
||||
{
|
||||
CV_Error(
|
||||
Error::StsBadArg,
|
||||
"Orders of dimensions in Permute layer parameter"
|
||||
"must be in [0...3] interval");
|
||||
}
|
||||
|
||||
if(std::find(_order.begin(), _order.end(), currentOrder) != _order.end())
|
||||
{
|
||||
CV_Error(Error::StsBadArg,
|
||||
"Permute layer parameter contains duplicated orders.");
|
||||
}
|
||||
}
|
||||
|
||||
void checkNeedForPermutation()
|
||||
{
|
||||
_needsPermute = false;
|
||||
for (size_t i = 0; i < _numAxes; ++i)
|
||||
{
|
||||
if (_order[i] != i)
|
||||
{
|
||||
_needsPermute = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PermuteLayerImpl(const LayerParams ¶ms)
|
||||
{
|
||||
if (!params.has("order"))
|
||||
{
|
||||
_needsPermute = false;
|
||||
return;
|
||||
}
|
||||
|
||||
DictValue paramOrder = params.get("order");
|
||||
if(paramOrder.size() > 4)
|
||||
{
|
||||
CV_Error(
|
||||
Error::StsBadArg,
|
||||
"Too many (> 4) orders of dimensions in Permute layer");
|
||||
}
|
||||
|
||||
_numAxes = paramOrder.size();
|
||||
|
||||
for (size_t i = 0; i < _numAxes; i++)
|
||||
{
|
||||
int currentOrder = paramOrder.get<int>(i);
|
||||
checkCurrentOrder(currentOrder);
|
||||
_order.push_back(currentOrder);
|
||||
}
|
||||
|
||||
setParamsFrom(params);
|
||||
checkNeedForPermutation();
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
if(!_needsPermute)
|
||||
return true;
|
||||
|
||||
CV_Assert(inputs.size() > 0);
|
||||
CV_Assert((int)_numAxes == inputs[0].size());
|
||||
|
||||
MatShape shapeBefore = inputs[0], shapeAfter;
|
||||
for (size_t i = 0; i < _numAxes; i++)
|
||||
{
|
||||
shapeAfter.push_back(shapeBefore[_order[i]]);
|
||||
}
|
||||
|
||||
outputs.clear();
|
||||
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
CV_Assert(inputs[i][2] == shapeBefore[2] && inputs[i][3] == shapeBefore[3]);
|
||||
CV_Assert(total(inputs[i]) == total(shapeAfter));
|
||||
outputs.push_back(shapeAfter);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void computeStrides(const MatShape &shapeBefore, const MatShape &shapeAfter)
|
||||
{
|
||||
_oldStride.resize(_numAxes);
|
||||
_newStride.resize(_numAxes);
|
||||
|
||||
_oldStride[_numAxes - 1] = 1;
|
||||
_newStride[_numAxes - 1] = 1;
|
||||
|
||||
for(int i = _numAxes - 2; i >= 0; i--)
|
||||
{
|
||||
_oldStride[i] = _oldStride[i + 1] * shapeBefore[i + 1];
|
||||
_newStride[i] = _newStride[i + 1] * shapeAfter[i + 1];
|
||||
}
|
||||
|
||||
_count = _oldStride[0] * shapeBefore[0];
|
||||
}
|
||||
|
||||
void finalize(const std::vector<Mat*> &inputs, std::vector<Mat> &outputs)
|
||||
{
|
||||
if(!_needsPermute)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CV_Assert(inputs.size() > 0);
|
||||
const Mat& inp0 = *inputs[0];
|
||||
CV_Assert((int)_numAxes == inp0.dims);
|
||||
|
||||
computeStrides(shape(*inputs[0]), shape(outputs[0]));
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
size_t k, ninputs = inputs.size();
|
||||
if(!_needsPermute)
|
||||
{
|
||||
for (k = 0; k < ninputs; k++)
|
||||
outputs[k] = *inputs[k];
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t i, j, count = _count, numAxes = _numAxes;
|
||||
const size_t* newStride = &_newStride[0];
|
||||
const size_t* oldStride = &_oldStride[0];
|
||||
const size_t* order = &_order[0];
|
||||
|
||||
for (k = 0; k < ninputs; k++)
|
||||
{
|
||||
const Mat& inp = *inputs[k];
|
||||
Mat& out = outputs[k];
|
||||
|
||||
CV_Assert(inp.dims == numAxes && inp.size == inputs[0]->size);
|
||||
CV_Assert(out.dims == numAxes && out.size == outputs[0].size);
|
||||
|
||||
// for( i = 0; i < numAxes; i++ )
|
||||
// {
|
||||
// CV_Assert(inp.size[i] == _oldDimensionSize[i]);
|
||||
// CV_Assert(out.size[i] == _newDimensionSize[i]);
|
||||
// }
|
||||
|
||||
CV_Assert(inp.isContinuous() && out.isContinuous());
|
||||
CV_Assert(inp.type() == CV_32F && out.type() == CV_32F);
|
||||
|
||||
const float *srcData = inp.ptr<float>();
|
||||
float *dstData = out.ptr<float>();
|
||||
|
||||
for (i = 0; i < count; ++i)
|
||||
{
|
||||
size_t oldPosition = 0;
|
||||
size_t newPosition = i;
|
||||
|
||||
for (j = 0; j < numAxes; ++j)
|
||||
{
|
||||
oldPosition += (newPosition / newStride[j]) * oldStride[order[j]];
|
||||
newPosition %= newStride[j];
|
||||
}
|
||||
dstData[i] = srcData[oldPosition];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
size_t _count;
|
||||
std::vector<size_t> _order;
|
||||
|
||||
std::vector<int> _oldDimensionSize;
|
||||
std::vector<int> _newDimensionSize;
|
||||
|
||||
std::vector<size_t> _oldStride;
|
||||
std::vector<size_t> _newStride;
|
||||
bool _needsPermute;
|
||||
|
||||
size_t _numAxes;
|
||||
};
|
||||
|
||||
Ptr<PermuteLayer> PermuteLayer::create(const LayerParams ¶ms)
|
||||
{
|
||||
return Ptr<PermuteLayer>(new PermuteLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,566 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "opencv2/core/hal/intrin.hpp"
|
||||
#include "op_halide.hpp"
|
||||
#include <float.h>
|
||||
#include <algorithm>
|
||||
using std::max;
|
||||
using std::min;
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
//TODO: add ceil_mode param
|
||||
class PoolingLayerImpl : public PoolingLayer
|
||||
{
|
||||
public:
|
||||
PoolingLayerImpl(const LayerParams& params)
|
||||
{
|
||||
type = PoolingLayer::MAX;
|
||||
computeMaxIdx = true;
|
||||
|
||||
if (params.has("pool"))
|
||||
{
|
||||
String pool = params.get<String>("pool").toLowerCase();
|
||||
if (pool == "max")
|
||||
type = PoolingLayer::MAX;
|
||||
else if (pool == "ave")
|
||||
type = PoolingLayer::AVE;
|
||||
else if (pool == "stochastic")
|
||||
type = PoolingLayer::STOCHASTIC;
|
||||
else
|
||||
CV_Error(Error::StsBadArg, "Unknown pooling type \"" + pool + "\"");
|
||||
}
|
||||
|
||||
getPoolingKernelParams(params, kernel.height, kernel.width, globalPooling,
|
||||
pad.height, pad.width, stride.height, stride.width, padMode);
|
||||
setParamsFrom(params);
|
||||
}
|
||||
|
||||
void finalize(const std::vector<Mat*> &inputs, std::vector<Mat> &outputs)
|
||||
{
|
||||
CV_Assert(inputs.size() == 1);
|
||||
|
||||
cv::Size inp(inputs[0]->size[3], inputs[0]->size[2]),
|
||||
out(outputs[0].size[3], outputs[0].size[2]);
|
||||
|
||||
if(globalPooling)
|
||||
{
|
||||
kernel = inp;
|
||||
}
|
||||
|
||||
getConvPoolPaddings(inp, out, kernel, stride, padMode, pad);
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide() &&
|
||||
(type == PoolingLayer::MAX ||
|
||||
type == PoolingLayer::AVE && !pad.width && !pad.height);
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
for (size_t ii = 0; ii < inputs.size(); ii++)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MAX:
|
||||
maxPooling(*inputs[ii], outputs[2 * ii], outputs[2 * ii + 1]);
|
||||
break;
|
||||
case AVE:
|
||||
avePooling(*inputs[ii], outputs[ii]);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsNotImplemented, "Not implemented");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
if (type == PoolingLayer::MAX)
|
||||
return initMaxPoolingHalide(inputs);
|
||||
else if (type == PoolingLayer::AVE)
|
||||
return initAvePoolingHalide(inputs);
|
||||
else
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
class MaxPoolingInvoker : public ParallelLoopBody
|
||||
{
|
||||
public:
|
||||
const Mat* src_;
|
||||
Mat *dst_, *mask_;
|
||||
Size kernel_, stride_, pad_;
|
||||
int nstripes_;
|
||||
bool computeMaxIdx_;
|
||||
|
||||
MaxPoolingInvoker(const Mat& src, Mat& dst, Mat& mask, Size kernel,
|
||||
Size stride, Size pad, int nstripes, bool computeMaxIdx)
|
||||
{
|
||||
src_ = &src;
|
||||
dst_ = &dst;
|
||||
mask_ = &mask;
|
||||
kernel_ = kernel;
|
||||
stride_ = stride;
|
||||
pad_ = pad;
|
||||
nstripes_ = nstripes;
|
||||
computeMaxIdx_ = computeMaxIdx;
|
||||
|
||||
CV_Assert(src.isContinuous() && dst.isContinuous() &&
|
||||
src.type() == CV_32F && src.type() == dst.type() &&
|
||||
mask.type() == src.type() && src.dims == 4 && dst.dims == 4 &&
|
||||
src.size[0] == dst.size[0] && src.size[1] == dst.size[1] &&
|
||||
mask.size == dst.size);
|
||||
}
|
||||
|
||||
void operator()(const Range& r) const
|
||||
{
|
||||
int nimgs = dst_->size[0], channels = dst_->size[1];
|
||||
int width = dst_->size[3], height = dst_->size[2];
|
||||
int inp_width = src_->size[3], inp_height = src_->size[2];
|
||||
size_t total = dst_->total();
|
||||
size_t stripeSize = (total + nstripes_ - 1)/nstripes_;
|
||||
size_t stripeStart = r.start*stripeSize;
|
||||
size_t stripeEnd = std::min(r.end*stripeSize, total);
|
||||
size_t ofs = stripeStart;
|
||||
int x0 = (int)(ofs % width);
|
||||
ofs /= width;
|
||||
int y0 = (int)(ofs % height);
|
||||
ofs /= height;
|
||||
int c = (int)(ofs % channels);
|
||||
int n = (int)(ofs / channels);
|
||||
const float *srcData = src_->ptr<float>(n, c);
|
||||
float *dstData = dst_->ptr<float>(n, c, y0) + x0;
|
||||
float *dstMaskData = mask_->ptr<float>(n, c, y0) + x0;
|
||||
int kernel_w = kernel_.width, kernel_h = kernel_.height;
|
||||
int pad_w = pad_.width, pad_h = pad_.height;
|
||||
int stride_w = stride_.width, stride_h = stride_.height;
|
||||
bool compMaxIdx = computeMaxIdx_;
|
||||
#if CV_SIMD128
|
||||
v_float32x4 idx00(0.f, (float)stride_w, (float)(stride_w*2), (float)(stride_w*3));
|
||||
v_float32x4 ones = v_setall_f32(1.f);
|
||||
v_float32x4 delta = v_setall_f32((float)(inp_width - kernel_w));
|
||||
#endif
|
||||
|
||||
for( ofs = stripeStart; ofs < stripeEnd; ofs++ )
|
||||
{
|
||||
int ystart = y0 * stride_h - pad_h;
|
||||
int xstart = x0 * stride_w - pad_w;
|
||||
int yend = min(ystart + kernel_h, inp_height);
|
||||
int xend = min(xstart + kernel_w, inp_width);
|
||||
ystart = max(ystart, 0);
|
||||
xstart = max(xstart, 0);
|
||||
float max_val = -FLT_MAX;
|
||||
int max_index = -1;
|
||||
|
||||
#if CV_SIMD128
|
||||
if( xstart > 0 && (x0 + 7) * stride_w - pad_w + kernel_w < inp_width )
|
||||
{
|
||||
if( compMaxIdx )
|
||||
{
|
||||
v_float32x4 max_val0 = v_setall_f32(max_val);
|
||||
v_float32x4 max_val1 = max_val0;
|
||||
v_float32x4 max_idx0 = v_setall_f32(-1.f);
|
||||
v_float32x4 max_idx1 = max_idx0;
|
||||
int index0 = ystart * inp_width + xstart;
|
||||
v_float32x4 idx0 = idx00 + v_setall_f32((float)index0);
|
||||
v_float32x4 idx1 = idx0 + v_setall_f32((float)(stride_w*4));
|
||||
|
||||
for (int y = ystart; y < yend; ++y)
|
||||
{
|
||||
for (int x = xstart; x < xend; ++x, idx0 += ones, idx1 += ones)
|
||||
{
|
||||
const int index = y * inp_width + x;
|
||||
v_float32x4 v0(srcData[index], srcData[index + stride_w],
|
||||
srcData[index + stride_w*2], srcData[index + stride_w*3]);
|
||||
v_float32x4 v1(srcData[index + stride_w*4], srcData[index + stride_w*5],
|
||||
srcData[index + stride_w*6], srcData[index + stride_w*7]);
|
||||
max_idx0 = v_select(v0 > max_val0, idx0, max_idx0);
|
||||
max_idx1 = v_select(v1 > max_val1, idx1, max_idx1);
|
||||
max_val0 = v_max(max_val0, v0);
|
||||
max_val1 = v_max(max_val1, v1);
|
||||
}
|
||||
idx0 += delta;
|
||||
idx1 += delta;
|
||||
}
|
||||
v_store(dstData, max_val0);
|
||||
v_store(dstData + 4, max_val1);
|
||||
v_store(dstMaskData, max_idx0);
|
||||
v_store(dstMaskData + 4, max_idx1);
|
||||
ofs += 7;
|
||||
dstData += 8;
|
||||
dstMaskData += 8;
|
||||
x0 += 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
v_float32x4 max_val0 = v_setall_f32(max_val);
|
||||
v_float32x4 max_val1 = max_val0;
|
||||
|
||||
for (int y = ystart; y < yend; ++y)
|
||||
{
|
||||
for (int x = xstart; x < xend; ++x)
|
||||
{
|
||||
const int index = y * inp_width + x;
|
||||
v_float32x4 v0(srcData[index], srcData[index + stride_w],
|
||||
srcData[index + stride_w*2], srcData[index + stride_w*3]);
|
||||
v_float32x4 v1(srcData[index + stride_w*4], srcData[index + stride_w*5],
|
||||
srcData[index + stride_w*6], srcData[index + stride_w*7]);
|
||||
max_val0 = v_max(max_val0, v0);
|
||||
max_val1 = v_max(max_val1, v1);
|
||||
}
|
||||
}
|
||||
v_store(dstData, max_val0);
|
||||
v_store(dstData + 4, max_val1);
|
||||
ofs += 7;
|
||||
dstData += 8;
|
||||
x0 += 7;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if( compMaxIdx )
|
||||
{
|
||||
for (int y = ystart; y < yend; ++y)
|
||||
for (int x = xstart; x < xend; ++x)
|
||||
{
|
||||
const int index = y * inp_width + x;
|
||||
float val = srcData[index];
|
||||
if (val > max_val)
|
||||
{
|
||||
max_val = val;
|
||||
max_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
*dstData++ = max_val;
|
||||
*dstMaskData++ = max_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int y = ystart; y < yend; ++y)
|
||||
for (int x = xstart; x < xend; ++x)
|
||||
{
|
||||
const int index = y * inp_width + x;
|
||||
float val = srcData[index];
|
||||
max_val = std::max(max_val, val);
|
||||
}
|
||||
|
||||
*dstData++ = max_val;
|
||||
}
|
||||
}
|
||||
|
||||
if( ++x0 >= width )
|
||||
{
|
||||
x0 = 0;
|
||||
if( ++y0 >= height )
|
||||
{
|
||||
y0 = 0;
|
||||
if( ++c >= channels )
|
||||
{
|
||||
c = 0;
|
||||
if( ++n >= nimgs )
|
||||
break;
|
||||
}
|
||||
srcData = src_->ptr<float>(n, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void maxPooling(Mat &src, Mat &dst, Mat &mask)
|
||||
{
|
||||
const int nstripes = getNumThreads();
|
||||
MaxPoolingInvoker mp(src, dst, mask, kernel, stride, pad, nstripes, computeMaxIdx);
|
||||
parallel_for_(Range(0, nstripes), mp, nstripes);
|
||||
}
|
||||
|
||||
void avePooling(Mat &src, Mat &dst)
|
||||
{
|
||||
Size inp(src.size[3], src.size[2]),
|
||||
out(dst.size[3], dst.size[2]);
|
||||
for (int n = 0; n < src.size[0]; ++n)
|
||||
{
|
||||
for (int c = 0; c < src.size[1]; ++c)
|
||||
{
|
||||
const float *srcData = src.ptr<float>(n, c);
|
||||
float *dstData = dst.ptr<float>(n, c);
|
||||
|
||||
for (int ph = 0; ph < out.height; ++ph)
|
||||
{
|
||||
for (int pw = 0; pw < out.width; ++pw)
|
||||
{
|
||||
int hstart = ph * stride.height - pad.height;
|
||||
int wstart = pw * stride.width - pad.width;
|
||||
int hend = min(hstart + kernel.height, inp.height + pad.height);
|
||||
int wend = min(wstart + kernel.width, inp.width + pad.width);
|
||||
int poolSize = (hend - hstart) * (wend - wstart);
|
||||
hstart = max(hstart, 0);
|
||||
wstart = max(wstart, 0);
|
||||
hend = min(hend, inp.height);
|
||||
wend = min(wend, inp.width);
|
||||
|
||||
dstData[ph * out.width + pw] = 0.f;
|
||||
|
||||
for (int h = hstart; h < hend; ++h)
|
||||
for (int w = wstart; w < wend; ++w)
|
||||
dstData[ph * out.width + pw] += srcData[h * inp.width + w];
|
||||
|
||||
dstData[ph * out.width + pw] /= poolSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initMaxPoolingHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
Halide::Buffer<float> inputBuffer = halideBuffer(inputs[0]);
|
||||
const int inWidth = inputBuffer.width();
|
||||
const int inHeight = inputBuffer.height();
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::RDom r(0, kernel.width, 0, kernel.height);
|
||||
Halide::Expr kx, ky;
|
||||
if (pad.width || pad.height)
|
||||
{
|
||||
kx = clamp(x * stride.width + r.x - pad.width, 0, inWidth - 1);
|
||||
ky = clamp(y * stride.height + r.y - pad.height, 0, inHeight - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
kx = min(x * stride.width + r.x, inWidth - 1);
|
||||
ky = min(y * stride.height + r.y, inHeight - 1);
|
||||
}
|
||||
|
||||
// Halide::argmax returns tuple (r.x, r.y, max).
|
||||
Halide::Tuple res = argmax(inputBuffer(kx, ky, c, n));
|
||||
|
||||
// Compute offset from argmax in range [0, kernel_size).
|
||||
Halide::Expr max_index;
|
||||
if (pad.width || pad.height)
|
||||
{
|
||||
max_index = clamp(y * stride.height + res[1] - pad.height,
|
||||
0, inHeight - 1) * inWidth +
|
||||
clamp(x * stride.width + res[0] - pad.width,
|
||||
0, inWidth - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
max_index = min(y * stride.height + res[1], inHeight - 1) * inWidth +
|
||||
min(x * stride.width + res[0], inWidth - 1);
|
||||
}
|
||||
top(x, y, c, n) = { res[2], Halide::cast<float>(max_index) };
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initAvePoolingHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
Halide::Buffer<float> inputBuffer = halideBuffer(inputs[0]);
|
||||
|
||||
const int inW = inputBuffer.width(), inH = inputBuffer.height();
|
||||
if ((inW - kernel.width) % stride.width || (inH - kernel.height) % stride.height)
|
||||
{
|
||||
CV_Error(cv::Error::StsNotImplemented,
|
||||
"Halide backend for average pooling with partial "
|
||||
"kernels is not implemented");
|
||||
}
|
||||
|
||||
const float norm = 1.0f / (kernel.width * kernel.height);
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::RDom r(0, kernel.width, 0, kernel.height);
|
||||
top(x, y, c, n) = sum(
|
||||
inputBuffer(x * stride.width + r.x,
|
||||
y * stride.height + r.y, c, n)) * norm;
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
virtual void applyHalideScheduler(Ptr<BackendNode>& node,
|
||||
const std::vector<Mat*> &inputs,
|
||||
const std::vector<Mat> &outputs,
|
||||
int targetId) const
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
if (targetId != DNN_TARGET_CPU)
|
||||
{
|
||||
Layer::applyHalideScheduler(node, inputs, outputs, targetId);
|
||||
return;
|
||||
}
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n"), tile("tile"),
|
||||
xi("xi"), yi("yi"), ci("ci"), xo("xo"), yo("yo"), co("co");
|
||||
Halide::Func& top = node.dynamicCast<HalideBackendNode>()->funcs.back();
|
||||
|
||||
int outW, outH, outC, outN;
|
||||
getCanonicalSize(outputs[0].size, &outW, &outH, &outC, &outN);
|
||||
|
||||
if (outW < 8 || outH < 8)
|
||||
{
|
||||
if (outC > 8)
|
||||
top.split(c, co, ci, 8)
|
||||
.fuse(x, y, tile).fuse(co, tile, tile).fuse(n, tile, tile)
|
||||
.parallel(tile)
|
||||
.vectorize(ci);
|
||||
else
|
||||
{
|
||||
top.fuse(y, c, tile).fuse(n, tile, tile)
|
||||
.parallel(tile);
|
||||
if (outW > 1)
|
||||
top.vectorize(x);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outC > 8)
|
||||
top.split(x, xo, xi, 8).split(y, yo, yi, 8).split(c, co, ci, 8)
|
||||
.fuse(xo, yo, tile).fuse(co, tile, tile).fuse(n, tile, tile)
|
||||
.parallel(tile)
|
||||
.vectorize(xi);
|
||||
else
|
||||
top.split(x, xo, xi, 8).split(y, yo, yi, 8)
|
||||
.fuse(xo, yo, tile).fuse(c, tile, tile).fuse(n, tile, tile)
|
||||
.parallel(tile)
|
||||
.vectorize(xi);
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() != 0);
|
||||
Size in(inputs[0][3], inputs[0][2]), out;
|
||||
|
||||
if (globalPooling)
|
||||
{
|
||||
out.height = 1;
|
||||
out.width = 1;
|
||||
}
|
||||
else if (padMode.empty())
|
||||
{
|
||||
//Yeah, something strange Caffe scheme-)
|
||||
out.height = static_cast<int>(ceil(static_cast<float>(in.height + 2 * pad.height -
|
||||
kernel.height) / stride.height)) + 1;
|
||||
out.width = static_cast<int>(ceil(static_cast<float>(in.width + 2 * pad.width -
|
||||
kernel.width) / stride.width)) + 1;
|
||||
|
||||
if (pad.height || pad.width)
|
||||
{
|
||||
// If we have padding, ensure that the last pooling starts strictly
|
||||
// inside the image (instead of at the padding); otherwise clip the last.
|
||||
if ((out.height - 1) * stride.height >= in.height + pad.height)
|
||||
--out.height;
|
||||
if ((out.width - 1) * stride.width >= in.width + pad.width)
|
||||
--out.width;
|
||||
CV_Assert((out.height - 1) * stride.height < in.height + pad.height);
|
||||
CV_Assert((out.width - 1) * stride.width < in.width + pad.width);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
getConvPoolOutParams(in, kernel, stride,
|
||||
padMode, out);
|
||||
}
|
||||
|
||||
outputs.resize(type == MAX ? 2 * inputs.size() : inputs.size());
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
size_t index = type == MAX ? 2*i : i;
|
||||
int dims[] = {inputs[i][0], inputs[i][1], out.height, out.width};
|
||||
outputs[index] = shape(dims);
|
||||
|
||||
if (type == MAX)
|
||||
outputs[index + 1] = shape(dims);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)inputs; // suppress unused variable warning
|
||||
long flops = 0;
|
||||
|
||||
for(int i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
if (type == MAX)
|
||||
{
|
||||
if (i%2 == 0)
|
||||
flops += total(outputs[i])*kernel.area();
|
||||
}
|
||||
else
|
||||
{
|
||||
flops += total(outputs[i])*(kernel.area() + 1);
|
||||
}
|
||||
}
|
||||
return flops;
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<PoolingLayer> PoolingLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<PoolingLayer>(new PoolingLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,379 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include <float.h>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class PriorBoxLayerImpl : public PriorBoxLayer
|
||||
{
|
||||
public:
|
||||
bool getParameterDict(const LayerParams ¶ms,
|
||||
const std::string ¶meterName,
|
||||
DictValue& result)
|
||||
{
|
||||
if (!params.has(parameterName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = params.get(parameterName);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T getParameter(const LayerParams ¶ms,
|
||||
const std::string ¶meterName,
|
||||
const size_t &idx=0,
|
||||
const bool required=true,
|
||||
const T& defaultValue=T())
|
||||
{
|
||||
DictValue dictValue;
|
||||
bool success = getParameterDict(params, parameterName, dictValue);
|
||||
if(!success)
|
||||
{
|
||||
if(required)
|
||||
{
|
||||
std::string message = _layerName;
|
||||
message += " layer parameter does not contain ";
|
||||
message += parameterName;
|
||||
message += " parameter.";
|
||||
CV_Error(Error::StsBadArg, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
return dictValue.get<T>(idx);
|
||||
}
|
||||
|
||||
void getAspectRatios(const LayerParams ¶ms)
|
||||
{
|
||||
DictValue aspectRatioParameter;
|
||||
bool aspectRatioRetieved = getParameterDict(params, "aspect_ratio", aspectRatioParameter);
|
||||
CV_Assert(aspectRatioRetieved);
|
||||
|
||||
for (int i = 0; i < aspectRatioParameter.size(); ++i)
|
||||
{
|
||||
float aspectRatio = aspectRatioParameter.get<float>(i);
|
||||
bool alreadyExists = false;
|
||||
|
||||
for (size_t j = 0; j < _aspectRatios.size(); ++j)
|
||||
{
|
||||
if (fabs(aspectRatio - _aspectRatios[j]) < 1e-6)
|
||||
{
|
||||
alreadyExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!alreadyExists)
|
||||
{
|
||||
_aspectRatios.push_back(aspectRatio);
|
||||
if (_flip)
|
||||
{
|
||||
_aspectRatios.push_back(1./aspectRatio);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void getVariance(const LayerParams ¶ms)
|
||||
{
|
||||
DictValue varianceParameter;
|
||||
bool varianceParameterRetrieved = getParameterDict(params, "variance", varianceParameter);
|
||||
CV_Assert(varianceParameterRetrieved);
|
||||
|
||||
int varianceSize = varianceParameter.size();
|
||||
if (varianceSize > 1)
|
||||
{
|
||||
// Must and only provide 4 variance.
|
||||
CV_Assert(varianceSize == 4);
|
||||
|
||||
for (int i = 0; i < varianceSize; ++i)
|
||||
{
|
||||
float variance = varianceParameter.get<float>(i);
|
||||
CV_Assert(variance > 0);
|
||||
_variance.push_back(variance);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (varianceSize == 1)
|
||||
{
|
||||
float variance = varianceParameter.get<float>(0);
|
||||
CV_Assert(variance > 0);
|
||||
_variance.push_back(variance);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Set default to 0.1.
|
||||
_variance.push_back(0.1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PriorBoxLayerImpl(const LayerParams ¶ms)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
_minSize = getParameter<unsigned>(params, "min_size");
|
||||
CV_Assert(_minSize > 0);
|
||||
|
||||
_flip = getParameter<bool>(params, "flip");
|
||||
_clip = getParameter<bool>(params, "clip");
|
||||
|
||||
_aspectRatios.clear();
|
||||
_aspectRatios.push_back(1.);
|
||||
|
||||
getAspectRatios(params);
|
||||
getVariance(params);
|
||||
|
||||
_numPriors = _aspectRatios.size();
|
||||
|
||||
_maxSize = -1;
|
||||
if (params.has("max_size"))
|
||||
{
|
||||
_maxSize = params.get("max_size").get<float>(0);
|
||||
CV_Assert(_maxSize > _minSize);
|
||||
|
||||
_numPriors += 1;
|
||||
}
|
||||
|
||||
if (params.has("step_h") || params.has("step_w")) {
|
||||
CV_Assert(!params.has("step"));
|
||||
_stepY = getParameter<float>(params, "step_h");
|
||||
CV_Assert(_stepY > 0.);
|
||||
_stepX = getParameter<float>(params, "step_w");
|
||||
CV_Assert(_stepX > 0.);
|
||||
} else if (params.has("step")) {
|
||||
const float step = getParameter<float>(params, "step");
|
||||
CV_Assert(step > 0);
|
||||
_stepY = step;
|
||||
_stepX = step;
|
||||
} else {
|
||||
_stepY = 0;
|
||||
_stepX = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() == 2);
|
||||
|
||||
int layerHeight = inputs[0][2];
|
||||
int layerWidth = inputs[0][3];
|
||||
|
||||
// Since all images in a batch has same height and width, we only need to
|
||||
// generate one set of priors which can be shared across all images.
|
||||
size_t outNum = 1;
|
||||
// 2 channels. First channel stores the mean of each prior coordinate.
|
||||
// Second channel stores the variance of each prior coordinate.
|
||||
size_t outChannels = 2;
|
||||
|
||||
outputs.resize(1, shape(outNum, outChannels,
|
||||
layerHeight * layerWidth * _numPriors * 4));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
int _layerWidth = inputs[0]->size[3];
|
||||
int _layerHeight = inputs[0]->size[2];
|
||||
|
||||
int _imageWidth = inputs[1]->size[3];
|
||||
int _imageHeight = inputs[1]->size[2];
|
||||
|
||||
float stepX, stepY;
|
||||
if (_stepX == 0 || _stepY == 0) {
|
||||
stepX = static_cast<float>(_imageWidth) / _layerWidth;
|
||||
stepY = static_cast<float>(_imageHeight) / _layerHeight;
|
||||
} else {
|
||||
stepX = _stepX;
|
||||
stepY = _stepY;
|
||||
}
|
||||
|
||||
int _outChannelSize = _layerHeight * _layerWidth * _numPriors * 4;
|
||||
|
||||
float* outputPtr = outputs[0].ptr<float>();
|
||||
|
||||
// first prior: aspect_ratio = 1, size = min_size
|
||||
int idx = 0;
|
||||
for (size_t h = 0; h < _layerHeight; ++h)
|
||||
{
|
||||
for (size_t w = 0; w < _layerWidth; ++w)
|
||||
{
|
||||
_boxWidth = _boxHeight = _minSize;
|
||||
|
||||
float center_x = (w + 0.5) * stepX;
|
||||
float center_y = (h + 0.5) * stepY;
|
||||
// xmin
|
||||
outputPtr[idx++] = (center_x - _boxWidth / 2.) / _imageWidth;
|
||||
// ymin
|
||||
outputPtr[idx++] = (center_y - _boxHeight / 2.) / _imageHeight;
|
||||
// xmax
|
||||
outputPtr[idx++] = (center_x + _boxWidth / 2.) / _imageWidth;
|
||||
// ymax
|
||||
outputPtr[idx++] = (center_y + _boxHeight / 2.) / _imageHeight;
|
||||
|
||||
if (_maxSize > 0)
|
||||
{
|
||||
// second prior: aspect_ratio = 1, size = sqrt(min_size * max_size)
|
||||
_boxWidth = _boxHeight = sqrt(_minSize * _maxSize);
|
||||
// xmin
|
||||
outputPtr[idx++] = (center_x - _boxWidth / 2.) / _imageWidth;
|
||||
// ymin
|
||||
outputPtr[idx++] = (center_y - _boxHeight / 2.) / _imageHeight;
|
||||
// xmax
|
||||
outputPtr[idx++] = (center_x + _boxWidth / 2.) / _imageWidth;
|
||||
// ymax
|
||||
outputPtr[idx++] = (center_y + _boxHeight / 2.) / _imageHeight;
|
||||
}
|
||||
|
||||
// rest of priors
|
||||
for (size_t r = 0; r < _aspectRatios.size(); ++r)
|
||||
{
|
||||
float ar = _aspectRatios[r];
|
||||
if (fabs(ar - 1.) < 1e-6)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_boxWidth = _minSize * sqrt(ar);
|
||||
_boxHeight = _minSize / sqrt(ar);
|
||||
// xmin
|
||||
outputPtr[idx++] = (center_x - _boxWidth / 2.) / _imageWidth;
|
||||
// ymin
|
||||
outputPtr[idx++] = (center_y - _boxHeight / 2.) / _imageHeight;
|
||||
// xmax
|
||||
outputPtr[idx++] = (center_x + _boxWidth / 2.) / _imageWidth;
|
||||
// ymax
|
||||
outputPtr[idx++] = (center_y + _boxHeight / 2.) / _imageHeight;
|
||||
}
|
||||
}
|
||||
}
|
||||
// clip the prior's coordidate such that it is within [0, 1]
|
||||
if (_clip)
|
||||
{
|
||||
for (size_t d = 0; d < _outChannelSize; ++d)
|
||||
{
|
||||
outputPtr[d] = std::min<float>(std::max<float>(outputPtr[d], 0.), 1.);
|
||||
}
|
||||
}
|
||||
// set the variance.
|
||||
outputPtr = outputs[0].ptr<float>(0, 1);
|
||||
if(_variance.size() == 1)
|
||||
{
|
||||
Mat secondChannel(outputs[0].size[2], outputs[0].size[3], CV_32F, outputPtr);
|
||||
secondChannel.setTo(Scalar(_variance[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = 0;
|
||||
for (size_t h = 0; h < _layerHeight; ++h)
|
||||
{
|
||||
for (size_t w = 0; w < _layerWidth; ++w)
|
||||
{
|
||||
for (size_t i = 0; i < _numPriors; ++i)
|
||||
{
|
||||
for (int j = 0; j < 4; ++j)
|
||||
{
|
||||
outputPtr[count] = _variance[j];
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)outputs; // suppress unused variable warning
|
||||
long flops = 0;
|
||||
|
||||
for (int i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
flops += total(inputs[i], 2) * _numPriors * 4;
|
||||
}
|
||||
|
||||
return flops;
|
||||
}
|
||||
|
||||
float _minSize;
|
||||
float _maxSize;
|
||||
|
||||
float _boxWidth;
|
||||
float _boxHeight;
|
||||
|
||||
float _stepX, _stepY;
|
||||
|
||||
std::vector<float> _aspectRatios;
|
||||
std::vector<float> _variance;
|
||||
|
||||
bool _flip;
|
||||
bool _clip;
|
||||
|
||||
size_t _numPriors;
|
||||
|
||||
static const size_t _numAxes = 4;
|
||||
static const std::string _layerName;
|
||||
};
|
||||
|
||||
const std::string PriorBoxLayerImpl::_layerName = std::string("PriorBox");
|
||||
|
||||
Ptr<PriorBoxLayer> PriorBoxLayer::create(const LayerParams ¶ms)
|
||||
{
|
||||
return Ptr<PriorBoxLayer>(new PriorBoxLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,443 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <cmath>
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
template<typename Dtype>
|
||||
static void tanh(const Mat &src, Mat &dst)
|
||||
{
|
||||
MatConstIterator_<Dtype> itSrc = src.begin<Dtype>();
|
||||
MatIterator_<Dtype> itDst = dst.begin<Dtype>();
|
||||
|
||||
for (; itSrc != src.end<Dtype>(); itSrc++, itDst++)
|
||||
*itDst = std::tanh(*itSrc);
|
||||
}
|
||||
|
||||
//TODO: make utils method
|
||||
static void tanh(const Mat &src, Mat &dst)
|
||||
{
|
||||
dst.create(src.dims, (const int*)src.size, src.type());
|
||||
|
||||
if (src.type() == CV_32F)
|
||||
tanh<float>(src, dst);
|
||||
else if (src.type() == CV_64F)
|
||||
tanh<double>(src, dst);
|
||||
else
|
||||
CV_Error(Error::StsUnsupportedFormat, "Function supports only floating point types");
|
||||
}
|
||||
|
||||
static void sigmoid(const Mat &src, Mat &dst)
|
||||
{
|
||||
cv::exp(-src, dst);
|
||||
cv::pow(1 + dst, -1, dst);
|
||||
}
|
||||
|
||||
class LSTMLayerImpl : public LSTMLayer
|
||||
{
|
||||
int numTimeStamps, numSamples;
|
||||
bool allocated;
|
||||
|
||||
MatShape outTailShape; //shape of single output sample
|
||||
MatShape outTsShape; //shape of N output samples
|
||||
|
||||
bool useTimestampDim;
|
||||
bool produceCellOutput;
|
||||
|
||||
public:
|
||||
|
||||
LSTMLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
type = "LSTM";
|
||||
useTimestampDim = true;
|
||||
produceCellOutput = false;
|
||||
allocated = false;
|
||||
outTailShape.clear();
|
||||
}
|
||||
|
||||
void setUseTimstampsDim(bool use)
|
||||
{
|
||||
CV_Assert(!allocated);
|
||||
useTimestampDim = use;
|
||||
}
|
||||
|
||||
void setProduceCellOutput(bool produce)
|
||||
{
|
||||
CV_Assert(!allocated);
|
||||
produceCellOutput = produce;
|
||||
}
|
||||
|
||||
void setOutShape(const MatShape &outTailShape_)
|
||||
{
|
||||
CV_Assert(!allocated || total(outTailShape) == total(outTailShape_));
|
||||
outTailShape = outTailShape_;
|
||||
}
|
||||
|
||||
void setWeights(const Mat &Wh, const Mat &Wx, const Mat &bias)
|
||||
{
|
||||
CV_Assert(Wh.dims == 2 && Wx.dims == 2);
|
||||
CV_Assert(Wh.rows == Wx.rows);
|
||||
CV_Assert(Wh.rows == 4*Wh.cols);
|
||||
CV_Assert(Wh.rows == (int)bias.total());
|
||||
CV_Assert(Wh.type() == Wx.type() && Wx.type() == bias.type());
|
||||
|
||||
blobs.resize(3);
|
||||
blobs[0] = Mat(Wh.clone());
|
||||
blobs[1] = Mat(Wx.clone());
|
||||
blobs[2] = Mat(bias.clone()).reshape(1, 1);
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(blobs.size() == 3);
|
||||
CV_Assert(inputs.size() == 1);
|
||||
const MatShape& inp0 = inputs[0];
|
||||
|
||||
const Mat &Wh = blobs[0], &Wx = blobs[1];
|
||||
int _numOut = Wh.size[1];
|
||||
int _numInp = Wx.size[1];
|
||||
MatShape outTailShape_(outTailShape), outResShape;
|
||||
|
||||
if (!outTailShape_.empty())
|
||||
CV_Assert(total(outTailShape_) == _numOut);
|
||||
else
|
||||
outTailShape_.assign(1, _numOut);
|
||||
|
||||
int _numTimeStamps, _numSamples;
|
||||
if (useTimestampDim)
|
||||
{
|
||||
CV_Assert(inp0.size() >= 2 && total(inp0, 2) == _numInp);
|
||||
_numTimeStamps = inp0[0];
|
||||
_numSamples = inp0[1];
|
||||
outResShape.push_back(_numTimeStamps);
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Assert(inp0.size() >= 2 && total(inp0, 1) == _numInp);
|
||||
_numTimeStamps = 1;
|
||||
_numSamples = inp0[0];
|
||||
}
|
||||
|
||||
outResShape.push_back(_numSamples);
|
||||
outResShape.insert(outResShape.end(), outTailShape_.begin(), outTailShape_.end());
|
||||
|
||||
size_t noutputs = produceCellOutput ? 2 : 1;
|
||||
outputs.assign(noutputs, outResShape);
|
||||
|
||||
internals.assign(1, shape(_numSamples, _numOut)); // hInternal
|
||||
internals.push_back(shape(_numSamples, _numOut)); // cInternal
|
||||
internals.push_back(shape(_numSamples, 1)); // dummyOnes
|
||||
internals.push_back(shape(_numSamples, 4*_numOut)); // gates
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void finalize(const std::vector<Mat*> &input, std::vector<Mat> &output)
|
||||
{
|
||||
CV_Assert(blobs.size() == 3);
|
||||
CV_Assert(input.size() == 1);
|
||||
const Mat& inp0 = *input[0];
|
||||
|
||||
Mat &Wh = blobs[0], &Wx = blobs[1];
|
||||
int numOut = Wh.size[1];
|
||||
int numInp = Wx.size[1];
|
||||
|
||||
if (!outTailShape.empty())
|
||||
CV_Assert(total(outTailShape) == numOut);
|
||||
else
|
||||
outTailShape.assign(1, numOut);
|
||||
|
||||
if (useTimestampDim)
|
||||
{
|
||||
CV_Assert(inp0.dims >= 2 && (int)inp0.total(2) == numInp);
|
||||
numTimeStamps = inp0.size[0];
|
||||
numSamples = inp0.size[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Assert(inp0.dims >= 2 && (int)inp0.total(1) == numInp);
|
||||
numTimeStamps = 1;
|
||||
numSamples = inp0.size[0];
|
||||
}
|
||||
|
||||
outTsShape.clear();
|
||||
outTsShape.push_back(numSamples);
|
||||
outTsShape.insert(outTsShape.end(), outTailShape.begin(), outTailShape.end());
|
||||
|
||||
allocated = true;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &input, std::vector<Mat> &output, std::vector<Mat> &internals)
|
||||
{
|
||||
const Mat &Wh = blobs[0];
|
||||
const Mat &Wx = blobs[1];
|
||||
const Mat &bias = blobs[2];
|
||||
|
||||
int numOut = Wh.size[1];
|
||||
|
||||
Mat hInternal = internals[0], cInternal = internals[1],
|
||||
dummyOnes = internals[2], gates = internals[3];
|
||||
hInternal.setTo(0.);
|
||||
cInternal.setTo(0.);
|
||||
dummyOnes.setTo(1.);
|
||||
|
||||
int numSamplesTotal = numTimeStamps*numSamples;
|
||||
Mat xTs = input[0]->reshape(1, numSamplesTotal);
|
||||
|
||||
Mat hOutTs = output[0].reshape(1, numSamplesTotal);
|
||||
Mat cOutTs = produceCellOutput ? output[1].reshape(1, numSamplesTotal) : Mat();
|
||||
|
||||
for (int ts = 0; ts < numTimeStamps; ts++)
|
||||
{
|
||||
Range curRowRange(ts*numSamples, (ts + 1)*numSamples);
|
||||
Mat xCurr = xTs.rowRange(curRowRange);
|
||||
|
||||
gemm(xCurr, Wx, 1, gates, 0, gates, GEMM_2_T); // Wx * x_t
|
||||
gemm(hInternal, Wh, 1, gates, 1, gates, GEMM_2_T); //+Wh * h_{t-1}
|
||||
gemm(dummyOnes, bias, 1, gates, 1, gates); //+b
|
||||
|
||||
Mat getesIFO = gates.colRange(0, 3*numOut);
|
||||
Mat gateI = gates.colRange(0*numOut, 1*numOut);
|
||||
Mat gateF = gates.colRange(1*numOut, 2*numOut);
|
||||
Mat gateO = gates.colRange(2*numOut, 3*numOut);
|
||||
Mat gateG = gates.colRange(3*numOut, 4*numOut);
|
||||
|
||||
sigmoid(getesIFO, getesIFO);
|
||||
tanh(gateG, gateG);
|
||||
|
||||
//compute c_t
|
||||
multiply(gateF, cInternal, gateF); // f_t (*) c_{t-1}
|
||||
multiply(gateI, gateG, gateI); // i_t (*) g_t
|
||||
add(gateF, gateI, cInternal); // c_t = f_t (*) c_{t-1} + i_t (*) g_t
|
||||
|
||||
//compute h_t
|
||||
tanh(cInternal, hInternal);
|
||||
multiply(gateO, hInternal, hInternal);
|
||||
|
||||
//save results in output blobs
|
||||
hInternal.copyTo(hOutTs.rowRange(curRowRange));
|
||||
if (produceCellOutput)
|
||||
cInternal.copyTo(cOutTs.rowRange(curRowRange));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<LSTMLayer> LSTMLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<LSTMLayer>(new LSTMLayerImpl(params));
|
||||
}
|
||||
|
||||
int LSTMLayer::inputNameToIndex(String inputName)
|
||||
{
|
||||
if (inputName.toLowerCase() == "x")
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int LSTMLayer::outputNameToIndex(String outputName)
|
||||
{
|
||||
if (outputName.toLowerCase() == "h")
|
||||
return 0;
|
||||
else if (outputName.toLowerCase() == "c")
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
class RNNLayerImpl : public RNNLayer
|
||||
{
|
||||
int numX, numH, numO;
|
||||
int numSamples, numTimestamps, numSamplesTotal;
|
||||
int dtype;
|
||||
Mat Whh, Wxh, bh;
|
||||
Mat Who, bo;
|
||||
bool produceH;
|
||||
|
||||
public:
|
||||
|
||||
RNNLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
type = "RNN";
|
||||
produceH = false;
|
||||
}
|
||||
|
||||
void setProduceHiddenOutput(bool produce = false)
|
||||
{
|
||||
produceH = produce;
|
||||
}
|
||||
|
||||
void setWeights(const Mat &W_xh, const Mat &b_h, const Mat &W_hh, const Mat &W_ho, const Mat &b_o)
|
||||
{
|
||||
CV_Assert(W_hh.dims == 2 && W_xh.dims == 2);
|
||||
CV_Assert(W_hh.size[0] == W_xh.size[0] && W_hh.size[0] == W_hh.size[1] && (int)b_h.total() == W_xh.size[0]);
|
||||
CV_Assert(W_ho.size[0] == (int)b_o.total());
|
||||
CV_Assert(W_ho.size[1] == W_hh.size[1]);
|
||||
|
||||
blobs.resize(5);
|
||||
blobs[0] = Mat(W_xh.clone());
|
||||
blobs[1] = Mat(b_h.clone());
|
||||
blobs[2] = Mat(W_hh.clone());
|
||||
blobs[3] = Mat(W_ho.clone());
|
||||
blobs[4] = Mat(b_o.clone());
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() >= 1 && inputs.size() <= 2);
|
||||
|
||||
Mat Who_ = blobs[3];
|
||||
Mat Wxh_ = blobs[0];
|
||||
|
||||
int numTimestamps_ = inputs[0][0];
|
||||
int numSamples_ = inputs[0][1];
|
||||
|
||||
int numO_ = Who_.rows;
|
||||
int numH_ = Wxh_.rows;
|
||||
|
||||
outputs.clear();
|
||||
int dims[] = {numTimestamps_, numSamples_, numO_};
|
||||
outputs.push_back(shape(dims, 3));
|
||||
dims[2] = numH_;
|
||||
if (produceH)
|
||||
outputs.push_back(shape(dims, 3));
|
||||
|
||||
internals.assign(2, shape(numSamples_, numH_));
|
||||
internals.push_back(shape(numSamples_, 1));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void finalize(const std::vector<Mat*> &input, std::vector<Mat> &output)
|
||||
{
|
||||
CV_Assert(input.size() >= 1 && input.size() <= 2);
|
||||
|
||||
Wxh = blobs[0];
|
||||
bh = blobs[1];
|
||||
Whh = blobs[2];
|
||||
Who = blobs[3];
|
||||
bo = blobs[4];
|
||||
|
||||
numH = Wxh.rows;
|
||||
numX = Wxh.cols;
|
||||
numO = Who.rows;
|
||||
|
||||
const Mat& inp0 = *input[0];
|
||||
|
||||
CV_Assert(inp0.dims >= 2);
|
||||
CV_Assert(inp0.total(2) == numX);
|
||||
dtype = CV_32F;
|
||||
CV_Assert(inp0.type() == dtype);
|
||||
numTimestamps = inp0.size[0];
|
||||
numSamples = inp0.size[1];
|
||||
numSamplesTotal = numTimestamps * numSamples;
|
||||
|
||||
bh = bh.reshape(1, 1); //is 1 x numH Mat
|
||||
bo = bo.reshape(1, 1); //is 1 x numO Mat
|
||||
}
|
||||
|
||||
void reshapeOutput(std::vector<Mat> &output)
|
||||
{
|
||||
output.resize(produceH ? 2 : 1);
|
||||
int sz0[] = { numTimestamps, numSamples, numO };
|
||||
output[0].create(3, sz0, dtype);
|
||||
if (produceH)
|
||||
{
|
||||
int sz1[] = { numTimestamps, numSamples, numH };
|
||||
output[1].create(3, sz1, dtype);
|
||||
}
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &input, std::vector<Mat> &output, std::vector<Mat> &internals)
|
||||
{
|
||||
Mat xTs = input[0]->reshape(1, numSamplesTotal);
|
||||
Mat oTs = output[0].reshape(1, numSamplesTotal);
|
||||
Mat hTs = produceH ? output[1].reshape(1, numSamplesTotal) : Mat();
|
||||
Mat hCurr = internals[0];
|
||||
Mat hPrev = internals[1];
|
||||
Mat dummyBiasOnes = internals[2];
|
||||
|
||||
hPrev.setTo(0.);
|
||||
dummyBiasOnes.setTo(1.);
|
||||
|
||||
for (int ts = 0; ts < numTimestamps; ts++)
|
||||
{
|
||||
Range curRowRange = Range(ts * numSamples, (ts + 1) * numSamples);
|
||||
Mat xCurr = xTs.rowRange(curRowRange);
|
||||
|
||||
gemm(hPrev, Whh, 1, hCurr, 0, hCurr, GEMM_2_T); // W_{hh} * h_{prev}
|
||||
gemm(xCurr, Wxh, 1, hCurr, 1, hCurr, GEMM_2_T); //+W_{xh} * x_{curr}
|
||||
gemm(dummyBiasOnes, bh, 1, hCurr, 1, hCurr); //+bh
|
||||
tanh(hCurr, hPrev);
|
||||
|
||||
Mat oCurr = oTs.rowRange(curRowRange);
|
||||
gemm(hPrev, Who, 1, oCurr, 0, oCurr, GEMM_2_T); // W_{ho} * h_{prev}
|
||||
gemm(dummyBiasOnes, bo, 1, oCurr, 1, oCurr); //+b_o
|
||||
tanh(oCurr, oCurr);
|
||||
|
||||
if (produceH)
|
||||
hPrev.copyTo(hTs.rowRange(curRowRange));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
CV_EXPORTS_W Ptr<RNNLayer> RNNLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<RNNLayer>(new RNNLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
static void computeShapeByReshapeMask(const MatShape &srcShape,
|
||||
const MatShape &maskShape,
|
||||
Range srcRange /*= Range::all()*/,
|
||||
MatShape& dstShape)
|
||||
{
|
||||
int srcShapeSize = (int)srcShape.size();
|
||||
int maskShapeSize = (int)maskShape.size();
|
||||
|
||||
if (srcRange == Range::all())
|
||||
srcRange = Range(0, srcShapeSize);
|
||||
else
|
||||
{
|
||||
int sz = srcRange.size();
|
||||
srcRange.start = clamp(srcRange.start, srcShapeSize);
|
||||
srcRange.end = srcRange.end == INT_MAX ? srcShapeSize : srcRange.start + sz;
|
||||
}
|
||||
|
||||
bool explicitMask = !maskShape.empty(); // All mask values are positive.
|
||||
for (int i = 0, n = maskShape.size(); i < n && explicitMask; ++i)
|
||||
{
|
||||
explicitMask = maskShape[i] > 0;
|
||||
}
|
||||
// Working range of source shape is a range where area(src) == area(mask).
|
||||
if (explicitMask)
|
||||
{
|
||||
int maskTotal = total(maskShape);
|
||||
for (int i = srcRange.start + 1; i < srcRange.end; ++i)
|
||||
{
|
||||
if (total(srcShape, i, srcRange.end) != maskTotal)
|
||||
{
|
||||
srcRange.start = i - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CV_Assert(total(srcShape, srcRange.start, srcRange.end) == maskTotal);
|
||||
}
|
||||
|
||||
CV_Assert(0 <= srcRange.start && srcRange.start <= srcRange.end && srcRange.end <= srcShapeSize);
|
||||
int dstShapeSize = srcShapeSize - srcRange.size() + maskShapeSize;
|
||||
dstShape.resize(dstShapeSize);
|
||||
|
||||
std::copy(srcShape.begin(), srcShape.begin() + srcRange.start, dstShape.begin());
|
||||
std::copy(srcShape.begin() + srcRange.end, srcShape.begin() + srcShapeSize, dstShape.begin() + srcRange.start + maskShapeSize);
|
||||
|
||||
int inferDim = -1;
|
||||
for (int i = 0; i < maskShapeSize; i++)
|
||||
{
|
||||
if (maskShape[i] > 0)
|
||||
{
|
||||
dstShape[srcRange.start + i] = maskShape[i];
|
||||
}
|
||||
else if (maskShape[i] == 0)
|
||||
{
|
||||
if (srcRange.start + i >= srcShapeSize)
|
||||
CV_Error(Error::StsBadArg, format("Copy dim[%d] (which has zero size) is out of the source shape bounds", srcRange.start + i));
|
||||
dstShape[srcRange.start + i] = srcShape[srcRange.start + i];
|
||||
}
|
||||
else if (maskShape[i] == -1)
|
||||
{
|
||||
if (inferDim != -1)
|
||||
CV_Error(Error::StsAssert, "Duplicate of inferred dim (which is denoted by -1)");
|
||||
inferDim = srcRange.start + i;
|
||||
dstShape[inferDim] = 1;
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsBadArg, "maskShape[i] >= -1");
|
||||
}
|
||||
|
||||
size_t srcTotal = total(srcShape);
|
||||
size_t dstTotal = total(dstShape);
|
||||
|
||||
if (inferDim != -1)
|
||||
{
|
||||
if (srcTotal % dstTotal != 0)
|
||||
CV_Error(Error::StsBackTrace, "Can't infer a dim denoted by -1");
|
||||
|
||||
dstShape[inferDim] = (int)(srcTotal / dstTotal);
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Assert(srcTotal == dstTotal);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ReshapeLayerImpl : public ReshapeLayer
|
||||
{
|
||||
public:
|
||||
ReshapeLayerImpl(const LayerParams& params):
|
||||
performReordering(false)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
int axis = params.get<int>("axis", 0);
|
||||
int numAxes = params.get<int>("num_axes", -1);
|
||||
enableReordering = params.get<bool>("reorder_dims", false);
|
||||
CV_Assert(numAxes >= -1);
|
||||
newShapeRange = (numAxes == -1) ? Range(axis, INT_MAX) : Range(axis, axis + numAxes);
|
||||
|
||||
newShapeDesc.clear();
|
||||
if (params.has("dim"))
|
||||
{
|
||||
const DictValue ¶mShape = params.get("dim");
|
||||
int i, dims = paramShape.size();
|
||||
newShapeDesc.resize(dims);
|
||||
for (i = 0; i < dims; i++)
|
||||
newShapeDesc[i] = paramShape.get<int>(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
outputs.clear();
|
||||
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
outputs.push_back(MatShape());
|
||||
computeShapeByReshapeMask(inputs[i], newShapeDesc, newShapeRange, outputs.back());
|
||||
}
|
||||
internals = outputs;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void finalize(const std::vector<Mat*> &inputs, std::vector<Mat> &outputs)
|
||||
{
|
||||
CV_Assert(inputs.size());
|
||||
CV_Assert(outputs.size());
|
||||
Mat srcBlob = *inputs[0];
|
||||
int dims = srcBlob.dims;
|
||||
MatShape inputShape = shape(srcBlob), outShape = shape(outputs[0]);
|
||||
|
||||
// input.total() == output.total(). So if reordering is require,
|
||||
// one of the sizes will be are not equal.
|
||||
// Example where reordering is require: from 1x128x4x4 to 1x2048
|
||||
// Example where reordering is NOT require: from 1x1024x1x1 to 1x1024.
|
||||
bool reorderingRequire = false;
|
||||
const int minDims = min(dims, (int)outShape.size());
|
||||
for (int i = 0; !reorderingRequire && i < minDims; ++i)
|
||||
reorderingRequire = inputShape[i] != outShape[i];
|
||||
performReordering = enableReordering && reorderingRequire;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
for (size_t i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
Mat srcBlob = *inputs[i];
|
||||
MatShape inputShape = shape(srcBlob), outShape = shape(outputs[i]);
|
||||
|
||||
if (performReordering)
|
||||
{
|
||||
float *dstData = internals[i].ptr<float>();
|
||||
const float *srcData = srcBlob.ptr<float>();
|
||||
|
||||
int num = inputShape[0], channels = inputShape[1], height = inputShape[2], width = inputShape[3];
|
||||
int total = num*channels*height*width;
|
||||
for(int i_n = 0; i_n < num; i_n++) {
|
||||
for(int i_c = 0; i_c < channels; i_c++) {
|
||||
for(int i_h = 0; i_h < height; i_h++) {
|
||||
for(int i_w = 0; i_w < width; i_w++) {
|
||||
int src_i = channels*height*width*i_n + height*width*i_c + width*i_h + i_w;
|
||||
int dst_i = channels*height*width*i_n + i_c + channels*width*i_h + channels*i_w;
|
||||
|
||||
CV_Assert(dst_i < total);
|
||||
CV_Assert(src_i < total);
|
||||
|
||||
dstData[dst_i] = srcData[src_i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
internals[i].copyTo(outputs[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outputs[i].data != srcBlob.data)
|
||||
srcBlob.reshape(1, outShape).copyTo(outputs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::vector<int> > outShapes;
|
||||
bool enableReordering, performReordering;
|
||||
};
|
||||
|
||||
Ptr<ReshapeLayer> ReshapeLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<ReshapeLayer>(new ReshapeLayerImpl(params));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2016, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
/*
|
||||
Implementation of Scale layer.
|
||||
*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "op_halide.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class ScaleLayerImpl : public ScaleLayer
|
||||
{
|
||||
public:
|
||||
ScaleLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
hasBias = params.get<bool>("bias_term", false);
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide();
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
CV_Assert(blobs.size() == 1 + hasBias);
|
||||
|
||||
for (size_t ii = 0; ii < outputs.size(); ii++)
|
||||
{
|
||||
Mat &inpBlob = *inputs[ii];
|
||||
Mat &outBlob = outputs[ii];
|
||||
|
||||
CV_Assert(inpBlob.size[1] == blobs[0].total());
|
||||
if (hasBias)
|
||||
CV_Assert(inpBlob.size[1] == blobs[1].total());
|
||||
|
||||
CV_Assert(inpBlob.type() == CV_32F && outBlob.type() == CV_32F);
|
||||
|
||||
for( int cn = 0; cn < inpBlob.size[0]; cn++ )
|
||||
{
|
||||
for (int n = 0; n < inpBlob.size[1]; n++)
|
||||
{
|
||||
float w = blobs[0].at<float>(n);
|
||||
float b = hasBias ? blobs[1].at<float>(n) : 0;
|
||||
Mat outBlobPlane = getPlane(outBlob, cn, n);
|
||||
Mat inpBlobPlane = getPlane(inpBlob, cn, n);
|
||||
inpBlobPlane.convertTo(outBlobPlane, CV_32F, w, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> tryAttach(const Ptr<BackendNode>& node)
|
||||
{
|
||||
switch (node->backendId)
|
||||
{
|
||||
case DNN_BACKEND_HALIDE:
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
auto base = node.dynamicCast<HalideBackendNode>();
|
||||
Halide::Func& input = base->funcs.back();
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = attachHalide(input(x, y, c, n));
|
||||
return Ptr<BackendNode>(new HalideBackendNode(base, top));
|
||||
#endif // HAVE_HALIDE
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
Halide::Buffer<float> input = halideBuffer(inputs[0]);
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = attachHalide(input(x, y, c, n));
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
// attachHalide can work both with Halide::Buffer and Halide::Func. In the
|
||||
// second case it will be a fusion.
|
||||
Halide::Func attachHalide(const Halide::Expr& input)
|
||||
{
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
|
||||
const int numChannels = blobs[0].total();
|
||||
|
||||
auto weights = wrapToHalideBuffer(blobs[0], {numChannels});
|
||||
Halide::Expr topExpr = input * weights(c);
|
||||
if (hasBias)
|
||||
{
|
||||
auto bias = wrapToHalideBuffer(blobs[1], {numChannels});
|
||||
topExpr += bias(c);
|
||||
}
|
||||
top(x, y, c, n) = topExpr;
|
||||
return top;
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)outputs; // suppress unused variable warning
|
||||
long flops = 0;
|
||||
for(int i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
flops += 2*total(inputs[i]);
|
||||
}
|
||||
return flops;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Ptr<ScaleLayer> ScaleLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<ScaleLayer>(new ScaleLayerImpl(params));
|
||||
}
|
||||
|
||||
} // namespace dnn
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,96 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2016, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
/*
|
||||
Implementation of shift layer, which adds up const values to blob.
|
||||
*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class ShiftLayerImpl : public ShiftLayer
|
||||
{
|
||||
public:
|
||||
ShiftLayerImpl(const LayerParams ¶ms)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
CV_Assert(blobs.size() == 1);
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
|
||||
internals.assign(1, shape(1, total(inputs[0], 2)));
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
CV_Assert(inputs.size() > 0);
|
||||
CV_Assert(blobs.size() > 0);
|
||||
|
||||
if(inputs[0]->dims == blobs[0].dims)
|
||||
{
|
||||
for (size_t ii = 0; ii < outputs.size(); ii++)
|
||||
{
|
||||
Mat &inpBlob = *inputs[ii];
|
||||
Mat &outBlob = outputs[ii];
|
||||
|
||||
outBlob = inpBlob + blobs[0];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Mat biasOnesMat = internals[0];
|
||||
biasOnesMat.setTo(1);
|
||||
for (size_t ii = 0; ii < outputs.size(); ii++)
|
||||
{
|
||||
Mat &inpBlob = *inputs[ii];
|
||||
Mat &outBlob = outputs[ii];
|
||||
|
||||
inpBlob.copyTo(outBlob);
|
||||
|
||||
for (int n = 0; n < inpBlob.size[0]; n++)
|
||||
{
|
||||
Mat dstMat(inpBlob.size[1], inpBlob.size[2] * inpBlob.size[3],
|
||||
outBlob.type(), outBlob.ptr(n));
|
||||
gemm(blobs[0], biasOnesMat, 1, dstMat, 1, dstMat); //TODO: gemv
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)outputs; // suppress unused variable warning
|
||||
long flops = 0;
|
||||
|
||||
for(int i= 0; i < inputs.size(); i++)
|
||||
{
|
||||
flops += total(inputs[i]);
|
||||
}
|
||||
|
||||
return flops;
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<ShiftLayer> ShiftLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<ShiftLayer>(new ShiftLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include <opencv2/dnn/shape_utils.hpp>
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class SliceLayerImpl : public SliceLayer
|
||||
{
|
||||
public:
|
||||
SliceLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
axis = params.get<int>("axis", 1);
|
||||
|
||||
if (params.has("slice_point"))
|
||||
{
|
||||
const DictValue &indicesValue = params.get("slice_point");
|
||||
int i, n = indicesValue.size();
|
||||
sliceIndices.resize(n);
|
||||
for (i = 0; i < n; i++)
|
||||
sliceIndices[i] = indicesValue.get<int>(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() == 1);
|
||||
|
||||
outputs.clear();
|
||||
|
||||
MatShape inpShape = inputs[0];
|
||||
int cAxis = clamp(axis, inpShape.size());
|
||||
int axisSize = inpShape[cAxis];
|
||||
|
||||
if (sliceIndices.size()) //divide blob with respect to passed parameters
|
||||
{
|
||||
std::vector<int> outAxisSize;
|
||||
int prevSlice = 0;
|
||||
|
||||
for (size_t i = 0; i < sliceIndices.size(); i++)
|
||||
{
|
||||
if (!(prevSlice < sliceIndices[i] && sliceIndices[i] < axisSize))
|
||||
CV_Error(Error::StsBadArg, "Slice indices should be positive, increased and don't exceed size of sliced dimension");
|
||||
|
||||
outAxisSize.push_back(sliceIndices[i] - prevSlice);
|
||||
prevSlice = sliceIndices[i];
|
||||
}
|
||||
outAxisSize.push_back(axisSize - prevSlice);
|
||||
|
||||
for (size_t i = 0; i < outAxisSize.size(); i++)
|
||||
{
|
||||
inpShape[cAxis] = outAxisSize[i];
|
||||
outputs.push_back(inpShape);
|
||||
}
|
||||
}
|
||||
else //divide blob with respect to count of output blobs
|
||||
{
|
||||
CV_Assert(requiredOutputs > 0 && axisSize % requiredOutputs == 0);
|
||||
int outAxisSize = axisSize / (int)requiredOutputs;
|
||||
|
||||
for (size_t i = 0; i < requiredOutputs; i++)
|
||||
{
|
||||
inpShape[cAxis] = outAxisSize;
|
||||
outputs.push_back(inpShape);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
const Mat& inpMat = *inputs[0];
|
||||
std::vector<Range> ranges(inpMat.dims, Range::all());
|
||||
int cAxis = clamp(axis, inpMat.dims);
|
||||
|
||||
ranges[cAxis].start = 0;
|
||||
for (size_t i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
ranges[cAxis].end = ranges[cAxis].start + outputs[i].size[cAxis];
|
||||
inpMat(&ranges[0]).copyTo(outputs[i]);
|
||||
ranges[cAxis].start = ranges[cAxis].end;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<SliceLayer> SliceLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<SliceLayer>(new SliceLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "op_halide.hpp"
|
||||
#include <algorithm>
|
||||
#include <stdlib.h>
|
||||
using std::max;
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class SoftMaxLayerImpl : public SoftmaxLayer
|
||||
{
|
||||
public:
|
||||
|
||||
SoftMaxLayerImpl(const LayerParams& params)
|
||||
{
|
||||
axisRaw = params.get<int>("axis", 1);
|
||||
logSoftMax = params.get<int>("log_softmax", false);
|
||||
setParamsFrom(params);
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
bool inplace = Layer::getMemoryShapes(inputs, requiredOutputs, outputs, internals);
|
||||
MatShape shape = inputs[0];
|
||||
int cAxis = clamp(axisRaw, shape.size());
|
||||
shape[cAxis] = 1;
|
||||
internals.assign(1, shape);
|
||||
return inplace;
|
||||
}
|
||||
|
||||
virtual bool supportBackend(int backendId)
|
||||
{
|
||||
return backendId == DNN_BACKEND_DEFAULT ||
|
||||
backendId == DNN_BACKEND_HALIDE && haveHalide() && axisRaw == 1;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
const Mat &src = *inputs[0];
|
||||
Mat &dst = outputs[0];
|
||||
|
||||
int axis = clamp(axisRaw, src.dims);
|
||||
size_t outerSize = src.total(0, axis), channels = src.size[axis],
|
||||
innerSize = src.total(axis + 1);
|
||||
|
||||
CV_Assert(src.type() == CV_32F);
|
||||
CV_Assert(src.isContinuous() && dst.isContinuous());
|
||||
|
||||
const float *srcPtr = src.ptr<float>();
|
||||
float *dstPtr = dst.ptr<float>();
|
||||
float *bufPtr = internals[0].ptr<float>();
|
||||
|
||||
size_t outerStep = src.total(axis);
|
||||
size_t cnStep = src.total(axis + 1);
|
||||
|
||||
//compute max along axis
|
||||
for (size_t outerDim = 0; outerDim < outerSize; outerDim++)
|
||||
{
|
||||
size_t srcOffset = outerDim * outerStep;
|
||||
size_t bufOffset = outerDim * cnStep;
|
||||
|
||||
memcpy(bufPtr + bufOffset, srcPtr + srcOffset, innerSize * sizeof(float));
|
||||
|
||||
for (size_t cnDim = 1; cnDim < channels; cnDim++)
|
||||
{
|
||||
for (size_t i = 0; i < innerSize; i++)
|
||||
bufPtr[bufOffset + i] = std::max(bufPtr[bufOffset + i], srcPtr[srcOffset + cnDim * cnStep + i]);
|
||||
}
|
||||
}
|
||||
|
||||
//subtract max
|
||||
for (size_t outerDim = 0; outerDim < outerSize; outerDim++)
|
||||
{
|
||||
size_t srcOffset = outerDim * outerStep;
|
||||
size_t bufOffset = outerDim * cnStep;
|
||||
|
||||
for (size_t cnDim = 0; cnDim < channels; cnDim++)
|
||||
{
|
||||
for (size_t i = 0; i < innerSize; i++)
|
||||
dstPtr[srcOffset + cnDim * cnStep + i] = srcPtr[srcOffset + cnDim * cnStep + i] - bufPtr[bufOffset + i];
|
||||
}
|
||||
}
|
||||
|
||||
cv::exp(dst, dst);
|
||||
|
||||
for (size_t outerDim = 0; outerDim < outerSize; outerDim++)
|
||||
{
|
||||
size_t srcOffset = outerDim * outerStep;
|
||||
size_t bufOffset = outerDim * cnStep;
|
||||
|
||||
//sum exp along axis
|
||||
for (size_t i = 0; i < innerSize; i++)
|
||||
bufPtr[bufOffset + i] = 0.f;
|
||||
|
||||
for (size_t cnDim = 0; cnDim < channels; cnDim++)
|
||||
{
|
||||
for (size_t i = 0; i < innerSize; i++)
|
||||
bufPtr[bufOffset + i] += dstPtr[srcOffset + cnDim * cnStep + i];
|
||||
}
|
||||
|
||||
//divide by computed sum
|
||||
for (size_t cnDim = 0; cnDim < channels; cnDim++)
|
||||
{
|
||||
for (size_t i = 0; i < innerSize; i++)
|
||||
dstPtr[srcOffset + cnDim * cnStep + i] /= bufPtr[bufOffset + i];
|
||||
}
|
||||
if (logSoftMax)
|
||||
{
|
||||
for (size_t cnDim = 0; cnDim < channels; cnDim++)
|
||||
{
|
||||
for (size_t i = 0; i < innerSize; i++)
|
||||
dstPtr[srcOffset + cnDim * cnStep + i] = log(dstPtr[srcOffset + cnDim * cnStep + i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual Ptr<BackendNode> initHalide(const std::vector<Ptr<BackendWrapper> > &inputs)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
Halide::Buffer<float> inputBuffer = halideBuffer(inputs[0]);
|
||||
int inW, inH, inC, inN;
|
||||
getCanonicalSize(inputBuffer, &inW, &inH, &inC, &inN);
|
||||
|
||||
if (inW != 1 || inH != 1)
|
||||
CV_Error(cv::Error::StsNotImplemented,
|
||||
"Halide backend for SoftMax with spatial size "
|
||||
"more than 1x1 is not implemented");
|
||||
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
Halide::Func top = (name.empty() ? Halide::Func() : Halide::Func(name));
|
||||
|
||||
Halide::Func expInput("expInput");
|
||||
Halide::RDom r(0, inW, 0, inH, 0, inC);
|
||||
expInput(x, y, c, n) = exp(inputBuffer(x, y, c, n));
|
||||
Halide::Expr globalSum = sum(expInput(r.x, r.y, r.z, n));
|
||||
top(x, y, c, n) = expInput(x, y, c, n) / globalSum;
|
||||
return Ptr<BackendNode>(new HalideBackendNode(top));
|
||||
#endif // HAVE_HALIDE
|
||||
return Ptr<BackendNode>();
|
||||
}
|
||||
|
||||
int64 getFLOPS(const std::vector<MatShape> &inputs,
|
||||
const std::vector<MatShape> &outputs) const
|
||||
{
|
||||
(void)outputs; // suppress unused variable warning
|
||||
int64 flops = 0;
|
||||
|
||||
for (int i = 0; i < inputs.size(); i++)
|
||||
{
|
||||
flops += 4*total(inputs[i]);
|
||||
}
|
||||
|
||||
return flops;
|
||||
}
|
||||
|
||||
int axisRaw;
|
||||
};
|
||||
|
||||
Ptr<SoftmaxLayer> SoftmaxLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<SoftmaxLayer>(new SoftMaxLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
class SplitLayerImpl : public SplitLayer
|
||||
{
|
||||
public:
|
||||
SplitLayerImpl(const LayerParams ¶ms)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
//TODO: maybe "top_count" param is useless because it can be determined by output connections number
|
||||
if (params.has("top_count"))
|
||||
{
|
||||
outputsCount = params.get<int>("top_count");
|
||||
CV_Assert(outputsCount >= 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputsCount = -1;
|
||||
}
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape> &inputs,
|
||||
const int requiredOutputs,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const
|
||||
{
|
||||
CV_Assert(inputs.size() == 1);
|
||||
|
||||
Layer::getMemoryShapes(inputs, max(1, outputsCount >= 0 ? outputsCount : requiredOutputs),
|
||||
outputs, internals);
|
||||
return true;
|
||||
}
|
||||
|
||||
void forward(std::vector<Mat*> &inputs, std::vector<Mat> &outputs, std::vector<Mat> &internals)
|
||||
{
|
||||
for (size_t i = 0; i < outputs.size(); i++)
|
||||
{
|
||||
CV_Assert(inputs[0]->total() == outputs[i].total());
|
||||
if (outputs[i].data != inputs[0]->data)
|
||||
inputs[0]->copyTo(outputs[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<SplitLayer> SplitLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<SplitLayer>(new SplitLayerImpl(params));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2017, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#include "op_halide.hpp"
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
#include <HalideRuntimeOpenCL.h>
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
Halide::Buffer<float> wrapToHalideBuffer(const Mat& mat)
|
||||
{
|
||||
int n, c, w, h;
|
||||
getCanonicalSize(mat.size, &w, &h, &c, &n);
|
||||
return wrapToHalideBuffer(mat, {w, h, c, n});
|
||||
}
|
||||
|
||||
Halide::Buffer<float> wrapToHalideBuffer(const Mat& mat,
|
||||
const std::vector<int>& sizes)
|
||||
{
|
||||
Halide::Buffer<float> buffer((float*)mat.data, sizes);
|
||||
buffer.set_host_dirty(); // Indicate that data is on CPU.
|
||||
return buffer;
|
||||
}
|
||||
|
||||
Halide::Buffer<> halideBuffer(const Ptr<BackendWrapper>& ptr)
|
||||
{
|
||||
CV_Assert(!ptr.empty());
|
||||
return ptr.dynamicCast<HalideBackendWrapper>()->buffer;
|
||||
}
|
||||
|
||||
std::vector<Halide::Buffer<> > halideBuffers(const std::vector<Ptr<BackendWrapper> >& ptrs)
|
||||
{
|
||||
std::vector<Halide::Buffer<> > vec;
|
||||
vec.reserve(ptrs.size());
|
||||
for (const Ptr<BackendWrapper>& ptr : ptrs)
|
||||
{
|
||||
vec.push_back(halideBuffer(ptr));
|
||||
}
|
||||
return vec;
|
||||
}
|
||||
|
||||
void getCanonicalSize(const Halide::Buffer<>& buffer, int* width, int* height,
|
||||
int* channels, int* batch)
|
||||
{
|
||||
CV_Assert(buffer.dimensions() == 4);
|
||||
*width = buffer.extent(0);
|
||||
*height = buffer.extent(1);
|
||||
*channels = buffer.extent(2);
|
||||
*batch = buffer.extent(3);
|
||||
}
|
||||
|
||||
HalideBackendNode::HalideBackendNode(const Halide::Func& func)
|
||||
: BackendNode(DNN_BACKEND_HALIDE), funcs(1, func) {}
|
||||
|
||||
HalideBackendNode::HalideBackendNode(const std::vector<Halide::Func>& funcs)
|
||||
: BackendNode(DNN_BACKEND_HALIDE), funcs(funcs) {}
|
||||
|
||||
HalideBackendNode::HalideBackendNode(const Ptr<HalideBackendNode>& base,
|
||||
const Halide::Func& top)
|
||||
: BackendNode(DNN_BACKEND_HALIDE), funcs(base->funcs)
|
||||
{
|
||||
funcs.back() = top;
|
||||
}
|
||||
|
||||
HalideBackendWrapper::HalideBackendWrapper(int targetId, const cv::Mat& m)
|
||||
: BackendWrapper(DNN_BACKEND_HALIDE, targetId)
|
||||
{
|
||||
buffer = wrapToHalideBuffer(m);
|
||||
if (targetId == DNN_TARGET_CPU)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else if (targetId == DNN_TARGET_OPENCL)
|
||||
{
|
||||
buffer.copy_to_device(halide_opencl_device_interface());
|
||||
}
|
||||
else
|
||||
CV_Error(Error::StsNotImplemented, "Unknown target identifier");
|
||||
}
|
||||
|
||||
HalideBackendWrapper::HalideBackendWrapper(const Ptr<BackendWrapper>& base,
|
||||
const MatShape& shape)
|
||||
: BackendWrapper(DNN_BACKEND_HALIDE, base->targetId)
|
||||
{
|
||||
int w, h, c, n;
|
||||
getCanonicalSize(shape, &w, &h, &c, &n);
|
||||
Halide::Buffer<float> baseBuffer = halideBuffer(base);
|
||||
buffer = Halide::Buffer<float>((float*)baseBuffer.raw_buffer()->host,
|
||||
{w, h, c, n});
|
||||
if (baseBuffer.has_device_allocation())
|
||||
{
|
||||
buffer.raw_buffer()->device = baseBuffer.raw_buffer()->device;
|
||||
buffer.raw_buffer()->device_interface = baseBuffer.raw_buffer()->device_interface;
|
||||
buffer.set_device_dirty();
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.set_host_dirty(); // Indicate that data is on CPU.
|
||||
CV_Assert(targetId == DNN_TARGET_CPU);
|
||||
}
|
||||
}
|
||||
|
||||
void HalideBackendWrapper::copyToHost()
|
||||
{
|
||||
CV_Assert(targetId == DNN_TARGET_CPU || buffer.device_dirty());
|
||||
if (buffer.device_dirty())
|
||||
{
|
||||
buffer.device_sync();
|
||||
buffer.copy_to_host();
|
||||
}
|
||||
}
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
void getCanonicalSize(const MatSize& size, int* width, int* height,
|
||||
int* channels, int* batch)
|
||||
{
|
||||
const int dims = size.p[-1];
|
||||
CV_Assert(dims == 2 || dims == 4);
|
||||
*batch = size[0];
|
||||
*channels = size[1];
|
||||
if (dims == 4)
|
||||
{
|
||||
*width = size[3];
|
||||
*height = size[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
*width = 1;
|
||||
*height = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void getCanonicalSize(const MatShape& shape, int* width, int* height,
|
||||
int* channels, int* batch)
|
||||
{
|
||||
const int dims = shape.size();
|
||||
CV_Assert(dims == 2 || dims == 4);
|
||||
*batch = shape[0];
|
||||
*channels = shape[1];
|
||||
if (dims == 4)
|
||||
{
|
||||
*width = shape[3];
|
||||
*height = shape[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
*width = 1;
|
||||
*height = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void compileHalide(std::vector<Mat> &outputs, Ptr<BackendNode>& node, int targetId)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
CV_Assert(!node.empty());
|
||||
Halide::Func& top = node.dynamicCast<HalideBackendNode>()->funcs.back();
|
||||
|
||||
int outW, outH, outC, outN;
|
||||
Halide::Var x("x"), y("y"), c("c"), n("n");
|
||||
getCanonicalSize(outputs[0].size, &outW, &outH, &outC, &outN);
|
||||
top.bound(x, 0, outW).bound(y, 0, outH)
|
||||
.bound(c, 0, outC).bound(n, 0, outN);
|
||||
|
||||
Halide::Target target = Halide::get_host_target();
|
||||
target.set_feature(Halide::Target::NoAsserts);
|
||||
if (targetId == DNN_TARGET_OPENCL)
|
||||
{
|
||||
target.set_feature(Halide::Target::OpenCL);
|
||||
}
|
||||
CV_Assert(target.supported());
|
||||
top.compile_jit(target);
|
||||
#endif // HAVE_HALIDE
|
||||
}
|
||||
|
||||
void forwardHalide(std::vector<Ptr<BackendWrapper> > &outputs,
|
||||
const Ptr<BackendNode>& node)
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
CV_Assert(!node.empty());
|
||||
Halide::Func& top = node.dynamicCast<HalideBackendNode>()->funcs.back();
|
||||
auto outputBuffers = halideBuffers(outputs);
|
||||
top.realize(Halide::Realization(outputBuffers));
|
||||
#endif // HAVE_HALIDE
|
||||
}
|
||||
|
||||
bool haveHalide()
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif // HAVE_HALIDE
|
||||
}
|
||||
|
||||
} // namespace dnn
|
||||
} // namespace cv
|
||||
@@ -0,0 +1,84 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
//
|
||||
// Copyright (C) 2017, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#ifndef __OPENCV_DNN_OP_HALIDE_HPP__
|
||||
#define __OPENCV_DNN_OP_HALIDE_HPP__
|
||||
|
||||
#include "precomp.hpp"
|
||||
|
||||
#ifdef HAVE_HALIDE
|
||||
#include <Halide.h>
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
namespace cv
|
||||
{
|
||||
namespace dnn
|
||||
{
|
||||
#ifdef HAVE_HALIDE
|
||||
// Returns four-dimensional buffer with float32 type that wrap cv::Mat data.
|
||||
// No data copy here.
|
||||
Halide::Buffer<float> wrapToHalideBuffer(const Mat& mat);
|
||||
|
||||
Halide::Buffer<float> wrapToHalideBuffer(const Mat& mat,
|
||||
const std::vector<int>& shape);
|
||||
|
||||
// Extract batch size, number of channels, width and height from buffer.
|
||||
void getCanonicalSize(const Halide::Buffer<>& buffer, int* width, int* height,
|
||||
int* channels, int* batch);
|
||||
|
||||
// Cast pointer and create copy of Halide buffer. No data copy.
|
||||
Halide::Buffer<> halideBuffer(const Ptr<BackendWrapper>& ptr);
|
||||
|
||||
std::vector<Halide::Buffer<> > halideBuffers(const std::vector<Ptr<BackendWrapper> >& ptrs);
|
||||
|
||||
class HalideBackendNode : public BackendNode
|
||||
{
|
||||
public:
|
||||
HalideBackendNode(const Halide::Func& func);
|
||||
|
||||
HalideBackendNode(const std::vector<Halide::Func>& funcs);
|
||||
|
||||
// Initialize from the <base> node but replace last function to <top>.
|
||||
// It's using in case of layers fusing when we want to keep functions of
|
||||
// root layer but replace top by fused one (i.e. conv+padding to relu+padding).
|
||||
HalideBackendNode(const Ptr<HalideBackendNode>& base, const Halide::Func& top);
|
||||
|
||||
std::vector<Halide::Func> funcs;
|
||||
};
|
||||
|
||||
class HalideBackendWrapper : public BackendWrapper
|
||||
{
|
||||
public:
|
||||
HalideBackendWrapper(int targetId, const cv::Mat& m);
|
||||
|
||||
HalideBackendWrapper(const Ptr<BackendWrapper>& base, const MatShape& shape);
|
||||
|
||||
virtual void copyToHost();
|
||||
|
||||
Halide::Buffer<float> buffer;
|
||||
};
|
||||
#endif // HAVE_HALIDE
|
||||
|
||||
// Extract batch size, number of channels, width and height from MatSize.
|
||||
void getCanonicalSize(const MatSize& size, int* width, int* height,
|
||||
int* channels, int* batch);
|
||||
|
||||
void getCanonicalSize(const MatShape& shape, int* width, int* height,
|
||||
int* channels, int* batch);
|
||||
|
||||
// Realize Halide pipeline into output blobs.
|
||||
void forwardHalide(std::vector<Ptr<BackendWrapper> > &outputs,
|
||||
const Ptr<BackendNode>& node);
|
||||
|
||||
// Compile Halide pipeline to specific target. Use outputs to set bounds of functions.
|
||||
void compileHalide(std::vector<Mat> &outputs, Ptr<BackendNode>& node, int targetId);
|
||||
|
||||
bool haveHalide();
|
||||
} // namespace dnn
|
||||
} // namespace cv
|
||||
|
||||
#endif // __OPENCV_DNN_OP_HALIDE_HPP__
|
||||
@@ -0,0 +1,44 @@
|
||||
__kernel void ReLUForward(const int count, __global const T* in, __global T* out
|
||||
#ifndef RELU_NO_SLOPE
|
||||
, T negative_slope
|
||||
#endif
|
||||
) {
|
||||
int index = get_global_id(0);
|
||||
if(index < count)
|
||||
#ifndef RELU_NO_SLOPE
|
||||
out[index] = in[index] > 0 ? in[index] : in[index] * negative_slope;
|
||||
#else
|
||||
out[index] = in[index] > 0 ? in[index] : 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
__kernel void TanHForward(const int count, __global T* in, __global T* out) {
|
||||
int index = get_global_id(0);
|
||||
if(index < count)
|
||||
out[index] = tanh(in[index]);
|
||||
}
|
||||
|
||||
__kernel void SigmoidForward(const int count, __global const T* in, __global T* out) {
|
||||
int index = get_global_id(0);
|
||||
if(index < count)
|
||||
out[index] = 1. / (1. + exp(-in[index]));
|
||||
}
|
||||
|
||||
__kernel void BNLLForward(const int n, __global const T* in, __global T* out) {
|
||||
int index = get_global_id(0);
|
||||
if (index < n) {
|
||||
out[index] = in[index] > 0 ? in[index] + log(1. + exp(-in[index])) : log(1. + exp(in[index]));
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void AbsValForward(const int n, __global const T* in, __global T* out) {
|
||||
int index = get_global_id(0);
|
||||
if (index < n)
|
||||
out[index] = fabs(in[index]);
|
||||
}
|
||||
|
||||
__kernel void PowForward(const int n, __global const T* in, __global T* out, const T power, const T scale, const T shift) {
|
||||
int index = get_global_id(0);
|
||||
if (index < n)
|
||||
out[index] = pow(shift + scale * in[index], power);
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*************************************************************************************
|
||||
* Copyright (c) 2015, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
**************************************************************************************/
|
||||
|
||||
__kernel void col2im(const int n, __global const T* data_col, const int col_offset,
|
||||
const int height, const int width, const int channels,
|
||||
const int patch_h, const int patch_w,
|
||||
const int pad_h, const int pad_w,
|
||||
const int stride_h, const int stride_w,
|
||||
const int height_col, const int width_col,
|
||||
__global T* data_im, const int img_offset)
|
||||
{
|
||||
data_col = data_col + col_offset;
|
||||
data_im = data_im + img_offset;
|
||||
int index = get_global_id(0);
|
||||
if(index < n) {
|
||||
T val = 0;
|
||||
int w = index % width + pad_w;
|
||||
int h = (index / width) % height + pad_h;
|
||||
int c = index / (width * height);
|
||||
|
||||
// compute the start and end of the output
|
||||
int w_col_start = (w < patch_w) ? 0 : (w - patch_w) / stride_w + 1;
|
||||
int w_col_end = min(w / stride_w + 1, width_col);
|
||||
int h_col_start = (h < patch_h) ? 0 : (h - patch_h) / stride_h + 1;
|
||||
int h_col_end = min(h / stride_h + 1, height_col);
|
||||
|
||||
// equivalent implementation
|
||||
int offset =
|
||||
(c * patch_h * patch_w + h * patch_w + w) * height_col * width_col;
|
||||
int coeff_h_col = (1 - stride_h * patch_w * height_col) * width_col;
|
||||
int coeff_w_col = (1 - stride_w * height_col * width_col);
|
||||
for (int h_col = h_col_start; h_col < h_col_end; ++h_col) {
|
||||
for (int w_col = w_col_start; w_col < w_col_end; ++w_col) {
|
||||
val += data_col[offset + h_col * coeff_h_col + w_col * coeff_w_col];
|
||||
}
|
||||
}
|
||||
data_im[index] = val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
__kernel void im2col(__global const T *im_src, int im_src_offset,
|
||||
int channels, int height_inp, int width_inp,
|
||||
int kernel_h, int kernel_w, int pad_h, int pad_w, int stride_h, int stride_w,
|
||||
int height_out, int width_out,
|
||||
__global T *im_col, int im_col_offset
|
||||
)
|
||||
{
|
||||
int index = get_global_id(0);
|
||||
if (index >= height_out * width_out * channels)
|
||||
return;
|
||||
int j_out = index % width_out;
|
||||
int i_out = (index / width_out) % height_out;
|
||||
int c_inp = (index / width_out) / height_out;
|
||||
|
||||
int c_out = c_inp * kernel_h * kernel_w;
|
||||
int i_inp = i_out * stride_h - pad_h;
|
||||
int j_inp = j_out * stride_w - pad_w;
|
||||
|
||||
im_src += (c_inp * height_inp + i_inp) * width_inp + j_inp + im_src_offset;
|
||||
im_col += (c_out * height_out + i_out) * width_out + j_out + im_col_offset;
|
||||
|
||||
for (int ki = 0; ki < kernel_h; ++ki)
|
||||
for (int kj = 0; kj < kernel_w; ++kj) {
|
||||
int i = i_inp + ki;
|
||||
int j = j_inp + kj;
|
||||
*im_col = (i >= 0 && j >= 0 && i < height_inp && j < width_inp) ?
|
||||
im_src[ki * width_inp + kj] : 0;
|
||||
im_col += height_out * width_out;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*************************************************************************************
|
||||
* Copyright (c) 2015, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
**************************************************************************************/
|
||||
|
||||
__kernel void LRNComputeOutput(const int nthreads, __global T* in, __global T* scale, const T negative_beta, __global T* out) {
|
||||
int index = get_global_id(0);
|
||||
int tmp = get_global_size(0);
|
||||
for(index; index < nthreads; index += tmp)
|
||||
out[index] = in[index] * pow(scale[index], negative_beta);
|
||||
}
|
||||
|
||||
__kernel void LRNFillScale(const int nthreads, __global T* in, const int num, const int channels, const int height, const int width, const int size, const T alpha_over_size, const T k, __global T* scale) {
|
||||
int index = get_global_id(0);
|
||||
int tmp = get_global_size(0);
|
||||
for(index; index < nthreads; index += tmp) {
|
||||
// find out the local offset
|
||||
const int w = index % width;
|
||||
const int h = (index / width) % height;
|
||||
const int n = index / width / height;
|
||||
const int offset = (n * channels * height + h) * width + w;
|
||||
const int step = height * width;
|
||||
in = in + offset;
|
||||
scale = scale + offset;
|
||||
int head = 0;
|
||||
const int pre_pad = (size - 1) / 2;
|
||||
const int post_pad = size - pre_pad - 1;
|
||||
T accum_scale = 0;
|
||||
// fill the scale at [n, :, h, w]
|
||||
// accumulate values
|
||||
while (head < post_pad && head < channels) {
|
||||
accum_scale += in[head * step] * in[head * step];
|
||||
++head;
|
||||
}
|
||||
// both add and subtract
|
||||
while (head < channels) {
|
||||
accum_scale += in[head * step] * in[head * step];
|
||||
if (head - size >= 0) {
|
||||
accum_scale -= in[(head - size) * step]
|
||||
* in[(head - size) * step];
|
||||
}
|
||||
scale[(head - post_pad) * step] = k + accum_scale * alpha_over_size;
|
||||
++head;
|
||||
}
|
||||
// subtract only
|
||||
while (head < channels + post_pad) {
|
||||
if (head - size >= 0) {
|
||||
accum_scale -= in[(head - size) * step]
|
||||
* in[(head - size) * step];
|
||||
}
|
||||
scale[(head - post_pad) * step] = k + accum_scale * alpha_over_size;
|
||||
++head;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
/*************************************************************************************
|
||||
* Copyright (c) 2015, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
**************************************************************************************/
|
||||
|
||||
__kernel void MaxPoolForward(const int nthreads,
|
||||
__global T* bottom_data, const int num, const int channels, const int height, const int width,
|
||||
const int pooled_height, const int pooled_width, const int kernel_h, const int kernel_w,
|
||||
const int stride_h, const int stride_w, const int pad_h, const int pad_w,
|
||||
__global T* top_data
|
||||
#ifdef MASK
|
||||
, __global float* mask
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int index = get_global_id(0);
|
||||
int tmp = get_global_size(0);
|
||||
for(index; index < nthreads; index += tmp) {
|
||||
int pw = index % pooled_width;
|
||||
int ph = (index / pooled_width) % pooled_height;
|
||||
int c = (index / pooled_width / pooled_height) % channels;
|
||||
int n = index / pooled_width / pooled_height / channels;
|
||||
int hstart = ph * stride_h - pad_h;
|
||||
int wstart = pw * stride_w - pad_w;
|
||||
const int hend = min(hstart + kernel_h, height);
|
||||
const int wend = min(wstart + kernel_w, width);
|
||||
hstart = max(hstart, 0);
|
||||
wstart = max(wstart, 0);
|
||||
T maxval = -FLT_MAX;
|
||||
int maxidx = -1;
|
||||
bottom_data =
|
||||
bottom_data + (n * channels + c) * height * width;
|
||||
for (int h = hstart; h < hend; ++h) {
|
||||
for (int w = wstart; w < wend; ++w) {
|
||||
if (bottom_data[h * width + w] > maxval) {
|
||||
maxidx = h * width + w;
|
||||
maxval = bottom_data[maxidx];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
top_data[index] = maxval;
|
||||
|
||||
#ifdef MASK
|
||||
mask[index] = maxidx;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void AvePoolForward(const int nthreads,
|
||||
__global T* bottom_data, const int num, const int channels, const int height, const int width,
|
||||
const int pooled_height, const int pooled_width, const int kernel_h, const int kernel_w,
|
||||
const int stride_h, const int stride_w, const int pad_h, const int pad_w,
|
||||
__global T* top_data
|
||||
#ifdef MASK
|
||||
, __global float* mask // NOT USED
|
||||
#endif
|
||||
)
|
||||
{
|
||||
int index = get_global_id(0);
|
||||
int tmp = get_global_size(0);
|
||||
for(index; index < nthreads; index+=tmp) {
|
||||
int pw = index % pooled_width;
|
||||
int ph = (index / pooled_width) % pooled_height;
|
||||
int c = (index / pooled_width / pooled_height) % channels;
|
||||
int n = index / pooled_width / pooled_height / channels; int hstart = ph * stride_h - pad_h; int wstart = pw * stride_w - pad_w;
|
||||
int hend = min(hstart + kernel_h, height + pad_h);
|
||||
int wend = min(wstart + kernel_w, width + pad_w);
|
||||
const int pool_size = (hend - hstart) * (wend - wstart);
|
||||
hstart = max(hstart, 0);
|
||||
wstart = max(wstart, 0);
|
||||
hend = min(hend, height);
|
||||
wend = min(wend, width);
|
||||
T aveval = 0;
|
||||
bottom_data =
|
||||
bottom_data + (n * channels + c) * height * width;
|
||||
for (int h = hstart; h < hend; ++h) {
|
||||
for (int w = wstart; w < wend; ++w) {
|
||||
aveval += bottom_data[h * width + w];
|
||||
}
|
||||
}
|
||||
top_data[index] = aveval / pool_size;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*************************************************************************************
|
||||
* Copyright (c) 2015, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation and/or
|
||||
* other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
**************************************************************************************/
|
||||
|
||||
__kernel void kernel_channel_max(const int num, const int channels,
|
||||
const int spatial_dim, __global const T* data, __global T* out) {
|
||||
int index = get_global_id(0);
|
||||
if(index < num * spatial_dim) {
|
||||
int n = index / spatial_dim;
|
||||
int s = index % spatial_dim;
|
||||
T maxval = -FLT_MAX;
|
||||
for (int c = 0; c < channels; ++c) {
|
||||
maxval = max(data[(n * channels + c) * spatial_dim + s], maxval);
|
||||
}
|
||||
out[index] = maxval;
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void kernel_channel_subtract(const int count,
|
||||
const int num, const int channels,
|
||||
const int spatial_dim, __global const T* channel_max, __global T* data) {
|
||||
int index = get_global_id(0);
|
||||
if(index < count) {
|
||||
int n = index / channels / spatial_dim;
|
||||
int s = index % spatial_dim;
|
||||
data[index] -= channel_max[n * spatial_dim + s];
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void kernel_channel_sum(const int num, const int channels,
|
||||
const int spatial_dim, __global const T* data, __global T* channel_sum) {
|
||||
int index = get_global_id(0);
|
||||
if(index < num * spatial_dim) {
|
||||
int n = index / spatial_dim;
|
||||
int s = index % spatial_dim;
|
||||
T sum = 0;
|
||||
for (int c = 0; c < channels; ++c) {
|
||||
sum += data[(n * channels + c) * spatial_dim + s];
|
||||
}
|
||||
channel_sum[index] = sum;
|
||||
}
|
||||
}
|
||||
|
||||
__kernel void kernel_channel_div(const int count,
|
||||
const int num, const int channels,
|
||||
const int spatial_dim, __global const T* channel_sum, __global T* data) {
|
||||
int index = get_global_id(0);
|
||||
if(index < count) {
|
||||
int n = index / channels / spatial_dim;
|
||||
int s = index % spatial_dim;
|
||||
data[index] /= channel_sum[n * spatial_dim + s];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/*M///////////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
||||
//
|
||||
// By downloading, copying, installing or using the software you agree to this license.
|
||||
// If you do not agree to this license, do not download, install,
|
||||
// copy or use the software.
|
||||
//
|
||||
//
|
||||
// License Agreement
|
||||
// For Open Source Computer Vision Library
|
||||
//
|
||||
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without modification,
|
||||
// are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistribution's of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
//
|
||||
// * Redistribution's in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// * The name of the copyright holders may not be used to endorse or promote products
|
||||
// derived from this software without specific prior written permission.
|
||||
//
|
||||
// This software is provided by the copyright holders and contributors "as is" and
|
||||
// any express or implied warranties, including, but not limited to, the implied
|
||||
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
||||
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
||||
// indirect, incidental, special, exemplary, or consequential damages
|
||||
// (including, but not limited to, procurement of substitute goods or services;
|
||||
// loss of use, data, or profits; or business interruption) however caused
|
||||
// and on any theory of liability, whether in contract, strict liability,
|
||||
// or tort (including negligence or otherwise) arising in any way out of
|
||||
// the use of this software, even if advised of the possibility of such damage.
|
||||
//
|
||||
//M*/
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include "cvconfig.h"
|
||||
#include <opencv2/dnn.hpp>
|
||||
#include <opencv2/dnn/all_layers.hpp>
|
||||
@@ -0,0 +1,60 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package tensorflow;
|
||||
option cc_enable_arenas = true;
|
||||
option java_outer_classname = "AttrValueProtos";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "org.tensorflow.framework";
|
||||
|
||||
import "tensor.proto";
|
||||
import "tensor_shape.proto";
|
||||
import "types.proto";
|
||||
|
||||
// Protocol buffer representing the value for an attr used to configure an Op.
|
||||
// Comment indicates the corresponding attr type. Only the field matching the
|
||||
// attr type may be filled.
|
||||
message AttrValue {
|
||||
message ListValue {
|
||||
repeated bytes s = 2; // "list(string)"
|
||||
repeated int64 i = 3 [packed = true]; // "list(int)"
|
||||
repeated float f = 4 [packed = true]; // "list(float)"
|
||||
repeated bool b = 5 [packed = true]; // "list(bool)"
|
||||
repeated DataType type = 6 [packed = true]; // "list(type)"
|
||||
repeated TensorShapeProto shape = 7; // "list(shape)"
|
||||
repeated TensorProto tensor = 8; // "list(tensor)"
|
||||
// TODO(zhifengc/josh11b): implements list(func) if needed.
|
||||
}
|
||||
|
||||
oneof value {
|
||||
bytes s = 2; // "string"
|
||||
int64 i = 3; // "int"
|
||||
float f = 4; // "float"
|
||||
bool b = 5; // "bool"
|
||||
DataType type = 6; // "type"
|
||||
TensorShapeProto shape = 7; // "shape"
|
||||
TensorProto tensor = 8; // "tensor"
|
||||
ListValue list = 1; // any "list(...)"
|
||||
|
||||
// "func" represents a function. func.name is a function's name or
|
||||
// a primitive op's name. func.attr.first is the name of an attr
|
||||
// defined for that function. func.attr.second is the value for
|
||||
// that attr in the instantiation.
|
||||
NameAttrList func = 10;
|
||||
|
||||
// This is a placeholder only used in nodes defined inside a
|
||||
// function. It indicates the attr value will be supplied when
|
||||
// the function is instantiated. For example, let us suppose a
|
||||
// node "N" in function "FN". "N" has an attr "A" with value
|
||||
// placeholder = "foo". When FN is instantiated with attr "foo"
|
||||
// set to "bar", the instantiated node N's attr A will have been
|
||||
// given the value "bar".
|
||||
string placeholder = 9;
|
||||
}
|
||||
}
|
||||
|
||||
// A list of attr names and their values. The whole list is attached
|
||||
// with a string name. E.g., MatMul[T=float].
|
||||
message NameAttrList {
|
||||
string name = 1;
|
||||
map<string, AttrValue> attr = 2;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package tensorflow;
|
||||
option cc_enable_arenas = true;
|
||||
option java_outer_classname = "FunctionProtos";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "org.tensorflow.framework";
|
||||
|
||||
import "attr_value.proto";
|
||||
import "op_def.proto";
|
||||
|
||||
// A library is a set of named functions.
|
||||
message FunctionDefLibrary {
|
||||
repeated FunctionDef function = 1;
|
||||
repeated GradientDef gradient = 2;
|
||||
}
|
||||
|
||||
// A function can be instantiated when the runtime can bind every attr
|
||||
// with a value. When a GraphDef has a call to a function, it must
|
||||
// have binding for every attr defined in the signature.
|
||||
//
|
||||
// TODO(zhifengc):
|
||||
// * device spec, etc.
|
||||
message FunctionDef {
|
||||
// The definition of the function's name, arguments, return values,
|
||||
// attrs etc.
|
||||
OpDef signature = 1;
|
||||
|
||||
// The body of the function.
|
||||
repeated Node node = 2; // function.node.ret[*] are unique.
|
||||
|
||||
// A node is a multi-value assignment:
|
||||
// (ret[0], ret[1], ...) = func(arg[0], arg[1], ...)
|
||||
//
|
||||
// By convention, "func" is resolved by consulting with a user-defined
|
||||
// library first. If not resolved, "func" is assumed to be a builtin op.
|
||||
message Node {
|
||||
// This node produces multiple outputs. They are named ret[0],
|
||||
// ret[1], ..., etc.
|
||||
//
|
||||
// REQUIRES: function.node.ret[*] are unique across all nodes.
|
||||
// REQUIRES: ret.size == func/op def's number of output args.
|
||||
repeated string ret = 1;
|
||||
|
||||
// The op/function name.
|
||||
string op = 2;
|
||||
|
||||
// Arguments passed to this func/op.
|
||||
//
|
||||
// arg[i] must be either one of
|
||||
// function.signature.input_args[*].name or one of
|
||||
// function.node[*].ret[*].
|
||||
//
|
||||
// REQUIRES: arg.size == func/op def's number of input args.
|
||||
repeated string arg = 3;
|
||||
|
||||
// Control dependencies.
|
||||
//
|
||||
// dep[i] must be one of function.node[*].ret[*] or one of
|
||||
// function.signature.input_args[*].name.
|
||||
repeated string dep = 4;
|
||||
|
||||
// Attrs.
|
||||
//
|
||||
// 'attr' maps names defined by 'func's attr defs to attr values.
|
||||
// attr values may have placeholders which are substituted
|
||||
// recursively by concrete values when this node is instantiated.
|
||||
// These placeholders must name an attr listed in the FunctionDef's
|
||||
// signature.
|
||||
map<string, AttrValue> attr = 5;
|
||||
}
|
||||
}
|
||||
|
||||
// GradientDef defines the gradient function of a function defined in
|
||||
// a function library.
|
||||
//
|
||||
// A gradient function g (specified by gradient_func) for a function f
|
||||
// (specified by function_name) must follow the following:
|
||||
//
|
||||
// The function 'f' must be a numerical function which takes N inputs
|
||||
// and produces M outputs. Its gradient function 'g', which is a
|
||||
// function taking N + M inputs and produces N outputs.
|
||||
//
|
||||
// I.e. if we have
|
||||
// (y1, y2, ..., y_M) = f(x1, x2, ..., x_N),
|
||||
// then, g is
|
||||
// (dL/dx1, dL/dx2, ..., dL/dx_N) = g(x1, x2, ..., x_N,
|
||||
// dL/dy1, dL/dy2, ..., dL/dy_M),
|
||||
// where L is a scalar-value function of (x1, x2, ..., xN) (e.g., the
|
||||
// loss function). dL/dx_i is the partial derivative of L with respect
|
||||
// to x_i.
|
||||
message GradientDef {
|
||||
string function_name = 1; // The function name.
|
||||
string gradient_func = 2; // The gradient function's name.
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package tensorflow;
|
||||
option cc_enable_arenas = true;
|
||||
option java_outer_classname = "GraphProtos";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "org.tensorflow.framework";
|
||||
|
||||
import "attr_value.proto";
|
||||
import "function.proto";
|
||||
import "versions.proto";
|
||||
|
||||
// Represents the graph of operations
|
||||
message GraphDef {
|
||||
repeated NodeDef node = 1;
|
||||
|
||||
// Compatibility versions of the graph. See core/public/version.h for version
|
||||
// history. The GraphDef version is distinct from the TensorFlow version, and
|
||||
// each release of TensorFlow will support a range of GraphDef versions.
|
||||
VersionDef versions = 4;
|
||||
|
||||
// Deprecated single version field; use versions above instead. Since all
|
||||
// GraphDef changes before "versions" was introduced were forward
|
||||
// compatible, this field is entirely ignored.
|
||||
int32 version = 3 [deprecated = true];
|
||||
|
||||
// EXPERIMENTAL. DO NOT USE OR DEPEND ON THIS YET.
|
||||
//
|
||||
// "library" provides user-defined functions.
|
||||
//
|
||||
// Naming:
|
||||
// * library.function.name are in a flat namespace.
|
||||
// NOTE: We may need to change it to be hierarchical to support
|
||||
// different orgs. E.g.,
|
||||
// { "/google/nn", { ... }},
|
||||
// { "/google/vision", { ... }}
|
||||
// { "/org_foo/module_bar", {...}}
|
||||
// map<string, FunctionDefLib> named_lib;
|
||||
// * If node[i].op is the name of one function in "library",
|
||||
// node[i] is deemed as a function call. Otherwise, node[i].op
|
||||
// must be a primitive operation supported by the runtime.
|
||||
//
|
||||
//
|
||||
// Function call semantics:
|
||||
//
|
||||
// * The callee may start execution as soon as some of its inputs
|
||||
// are ready. The caller may want to use Tuple() mechanism to
|
||||
// ensure all inputs are ready in the same time.
|
||||
//
|
||||
// * The consumer of return values may start executing as soon as
|
||||
// the return values the consumer depends on are ready. The
|
||||
// consumer may want to use Tuple() mechanism to ensure the
|
||||
// consumer does not start until all return values of the callee
|
||||
// function are ready.
|
||||
FunctionDefLibrary library = 2;
|
||||
};
|
||||
|
||||
message NodeDef {
|
||||
// The name given to this operator. Used for naming inputs,
|
||||
// logging, visualization, etc. Unique within a single GraphDef.
|
||||
// Must match the regexp "[A-Za-z0-9.][A-Za-z0-9_./]*".
|
||||
string name = 1;
|
||||
|
||||
// The operation name. There may be custom parameters in attrs.
|
||||
// Op names starting with an underscore are reserved for internal use.
|
||||
string op = 2;
|
||||
|
||||
// Each input is "node:src_output" with "node" being a string name and
|
||||
// "src_output" indicating which output tensor to use from "node". If
|
||||
// "src_output" is 0 the ":0" suffix can be omitted. Regular inputs
|
||||
// may optionally be followed by control inputs that have the format
|
||||
// "^node".
|
||||
repeated string input = 3;
|
||||
|
||||
// A (possibly partial) specification for the device on which this
|
||||
// node should be placed.
|
||||
// The expected syntax for this string is as follows:
|
||||
//
|
||||
// DEVICE_SPEC ::= COLOCATED_NODE | PARTIAL_SPEC
|
||||
//
|
||||
// COLOCATED_NODE ::= "@" NODE_NAME // See NodeDef.name above.
|
||||
// PARTIAL_SPEC ::= ("/" CONSTRAINT) *
|
||||
// CONSTRAINT ::= ("job:" JOB_NAME)
|
||||
// | ("replica:" [1-9][0-9]*)
|
||||
// | ("task:" [1-9][0-9]*)
|
||||
// | ( ("gpu" | "cpu") ":" ([1-9][0-9]* | "*") )
|
||||
//
|
||||
// Valid values for this string include:
|
||||
// * "@other/node" (colocate with "other/node")
|
||||
// * "/job:worker/replica:0/task:1/gpu:3" (full specification)
|
||||
// * "/job:worker/gpu:3" (partial specification)
|
||||
// * "" (no specification)
|
||||
//
|
||||
// If the constraints do not resolve to a single device (or if this
|
||||
// field is empty or not present), the runtime will attempt to
|
||||
// choose a device automatically.
|
||||
string device = 4;
|
||||
|
||||
// Operation-specific graph-construction-time configuration.
|
||||
// Note that this should include all attrs defined in the
|
||||
// corresponding OpDef, including those with a value matching
|
||||
// the default -- this allows the default to change and makes
|
||||
// NodeDefs easier to interpret on their own. However, if
|
||||
// an attr with a default is not specified in this list, the
|
||||
// default will be used.
|
||||
// The "names" (keys) must match the regexp "[a-z][a-z0-9_]+" (and
|
||||
// one of the names from the corresponding OpDef's attr field).
|
||||
// The values must have a type matching the corresponding OpDef
|
||||
// attr's type field.
|
||||
// TODO(josh11b): Add some examples here showing best practices.
|
||||
map<string, AttrValue> attr = 5;
|
||||
};
|
||||
@@ -0,0 +1,157 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package tensorflow;
|
||||
option cc_enable_arenas = true;
|
||||
option java_outer_classname = "OpDefProtos";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "org.tensorflow.framework";
|
||||
|
||||
import "attr_value.proto";
|
||||
import "types.proto";
|
||||
|
||||
// Defines an operation. A NodeDef in a GraphDef specifies an Op by
|
||||
// using the "op" field which should match the name of a OpDef.
|
||||
message OpDef {
|
||||
// Op names starting with an underscore are reserved for internal use.
|
||||
// Names should be CamelCase and match the regexp "[A-Z][a-zA-Z0-9_]*".
|
||||
string name = 1;
|
||||
|
||||
// For describing inputs and outputs.
|
||||
message ArgDef {
|
||||
// Name for the input/output. Should match the regexp "[a-z][a-z0-9_]*".
|
||||
string name = 1;
|
||||
|
||||
// Human readable description.
|
||||
string description = 2;
|
||||
|
||||
// Describes the type of one or more tensors that are accepted/produced
|
||||
// by this input/output arg. The only legal combinations are:
|
||||
// * For a single tensor: either the "type" field is set or the
|
||||
// "type_attr" field is set to the name of an attr with type "type".
|
||||
// * For a sequence of tensors with the same type: the "number_attr"
|
||||
// field will be set to the name of an attr with type "int", and
|
||||
// either the "type" or "type_attr" field will be set as for
|
||||
// single tensors.
|
||||
// * For a sequence of tensors, the "type_list_attr" field will be set
|
||||
// to the name of an attr with type "list(type)".
|
||||
DataType type = 3;
|
||||
string type_attr = 4; // if specified, attr must have type "type"
|
||||
string number_attr = 5; // if specified, attr must have type "int"
|
||||
// If specified, attr must have type "list(type)", and none of
|
||||
// type, type_attr, and number_attr may be specified.
|
||||
string type_list_attr = 6;
|
||||
|
||||
// For inputs: if true, the inputs are required to be refs.
|
||||
// By default, inputs can be either refs or non-refs.
|
||||
// For outputs: if true, outputs are refs, otherwise they are not.
|
||||
bool is_ref = 16;
|
||||
};
|
||||
|
||||
// Description of the input(s).
|
||||
repeated ArgDef input_arg = 2;
|
||||
|
||||
// Description of the output(s).
|
||||
repeated ArgDef output_arg = 3;
|
||||
|
||||
// Description of the graph-construction-time configuration of this
|
||||
// Op. That is to say, this describes the attr fields that will
|
||||
// be specified in the NodeDef.
|
||||
message AttrDef {
|
||||
// A descriptive name for the argument. May be used, e.g. by the
|
||||
// Python client, as a keyword argument name, and so should match
|
||||
// the regexp "[a-z][a-z0-9_]+".
|
||||
string name = 1;
|
||||
|
||||
// One of the type names from attr_value.proto ("string", "list(string)",
|
||||
// "int", etc.).
|
||||
string type = 2;
|
||||
|
||||
// A reasonable default for this attribute if the user does not supply
|
||||
// a value. If not specified, the user must supply a value.
|
||||
AttrValue default_value = 3;
|
||||
|
||||
// Human-readable description.
|
||||
string description = 4;
|
||||
|
||||
// TODO(josh11b): bool is_optional?
|
||||
|
||||
// --- Constraints ---
|
||||
// These constraints are only in effect if specified. Default is no
|
||||
// constraints.
|
||||
|
||||
// For type == "int", this is a minimum value. For "list(___)"
|
||||
// types, this is the minimum length.
|
||||
bool has_minimum = 5;
|
||||
int64 minimum = 6;
|
||||
|
||||
// The set of allowed values. Has type that is the "list" version
|
||||
// of the "type" field above (uses the "list" field of AttrValue).
|
||||
// If type == "type" or "list(type)" above, then the "type" field
|
||||
// of "allowed_values.list" has the set of allowed DataTypes.
|
||||
// If type == "string" or "list(string)", then the "s" field of
|
||||
// "allowed_values.list" has the set of allowed strings.
|
||||
AttrValue allowed_values = 7;
|
||||
}
|
||||
repeated AttrDef attr = 4;
|
||||
|
||||
// Optional deprecation based on GraphDef versions.
|
||||
OpDeprecation deprecation = 8;
|
||||
|
||||
// One-line human-readable description of what the Op does.
|
||||
string summary = 5;
|
||||
|
||||
// Additional, longer human-readable description of what the Op does.
|
||||
string description = 6;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Which optimizations this operation can participate in.
|
||||
|
||||
// True if the operation is commutative ("op(a,b) == op(b,a)" for all inputs)
|
||||
bool is_commutative = 18;
|
||||
|
||||
// If is_aggregate is true, then this operation accepts N >= 2
|
||||
// inputs and produces 1 output all of the same type. Should be
|
||||
// associative and commutative, and produce output with the same
|
||||
// shape as the input. The optimizer may replace an aggregate op
|
||||
// taking input from multiple devices with a tree of aggregate ops
|
||||
// that aggregate locally within each device (and possibly within
|
||||
// groups of nearby devices) before communicating.
|
||||
// TODO(josh11b): Implement that optimization.
|
||||
bool is_aggregate = 16; // for things like add
|
||||
|
||||
// Other optimizations go here, like
|
||||
// can_alias_input, rewrite_when_output_unused, partitioning_strategy, etc.
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Optimization constraints.
|
||||
|
||||
// By default Ops may be moved between devices. Stateful ops should
|
||||
// either not be moved, or should only be moved if that state can also
|
||||
// be moved (e.g. via some sort of save / restore).
|
||||
// Stateful ops are guaranteed to never be optimized away by Common
|
||||
// Subexpression Elimination (CSE).
|
||||
bool is_stateful = 17; // for things like variables, queue
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Non-standard options.
|
||||
|
||||
// By default, all inputs to an Op must be initialized Tensors. Ops
|
||||
// that may initialize tensors for the first time should set this
|
||||
// field to true, to allow the Op to take an uninitialized Tensor as
|
||||
// input.
|
||||
bool allows_uninitialized_input = 19; // for Assign, etc.
|
||||
};
|
||||
|
||||
// Information about version-dependent deprecation of an op
|
||||
message OpDeprecation {
|
||||
// First GraphDef version at which the op is disallowed.
|
||||
int32 version = 1;
|
||||
|
||||
// Explanation of why it was deprecated and what to use instead.
|
||||
string explanation = 2;
|
||||
};
|
||||
|
||||
// A collection of OpDefs
|
||||
message OpList {
|
||||
repeated OpDef op = 1;
|
||||
};
|
||||
@@ -0,0 +1,68 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package tensorflow;
|
||||
option cc_enable_arenas = true;
|
||||
option java_outer_classname = "TensorProtos";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "org.tensorflow.framework";
|
||||
|
||||
import "tensor_shape.proto";
|
||||
import "types.proto";
|
||||
|
||||
// Protocol buffer representing a tensor.
|
||||
message TensorProto {
|
||||
DataType dtype = 1;
|
||||
|
||||
// Shape of the tensor. TODO(touts): sort out the 0-rank issues.
|
||||
TensorShapeProto tensor_shape = 2;
|
||||
|
||||
// Only one of the representations below is set, one of "tensor_contents" and
|
||||
// the "xxx_val" attributes. We are not using oneof because as oneofs cannot
|
||||
// contain repeated fields it would require another extra set of messages.
|
||||
|
||||
// Version number.
|
||||
//
|
||||
// In version 0, if the "repeated xxx" representations contain only one
|
||||
// element, that element is repeated to fill the shape. This makes it easy
|
||||
// to represent a constant Tensor with a single value.
|
||||
int32 version_number = 3;
|
||||
|
||||
// Serialized content from Tensor::AsProtoTensorContent(). This representation
|
||||
// can be used for all tensor types.
|
||||
bytes tensor_content = 4;
|
||||
|
||||
// Type specific representations that make it easy to create tensor protos in
|
||||
// all languages. Only the representation corresponding to "dtype" can
|
||||
// be set. The values hold the flattened representation of the tensor in
|
||||
// row major order.
|
||||
|
||||
// DT_HALF. Note that since protobuf has no int16 type, we'll have some
|
||||
// pointless zero padding for each value here.
|
||||
repeated int32 half_val = 13 [packed = true];
|
||||
|
||||
// DT_FLOAT.
|
||||
repeated float float_val = 5 [packed = true];
|
||||
|
||||
// DT_DOUBLE.
|
||||
repeated double double_val = 6 [packed = true];
|
||||
|
||||
// DT_INT32, DT_INT16, DT_INT8, DT_UINT8.
|
||||
repeated int32 int_val = 7 [packed = true];
|
||||
|
||||
// DT_STRING
|
||||
repeated bytes string_val = 8;
|
||||
|
||||
// DT_COMPLEX64. scomplex_val(2*i) and scomplex_val(2*i+1) are real
|
||||
// and imaginary parts of i-th single precision complex.
|
||||
repeated float scomplex_val = 9 [packed = true];
|
||||
|
||||
// DT_INT64
|
||||
repeated int64 int64_val = 10 [packed = true];
|
||||
|
||||
// DT_BOOL
|
||||
repeated bool bool_val = 11 [packed = true];
|
||||
|
||||
// DT_COMPLEX128. dcomplex_val(2*i) and dcomplex_val(2*i+1) are real
|
||||
// and imaginary parts of i-th double precision complex.
|
||||
repeated double dcomplex_val = 12 [packed = true];
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
// Protocol buffer representing the shape of tensors.
|
||||
|
||||
syntax = "proto3";
|
||||
option cc_enable_arenas = true;
|
||||
option java_outer_classname = "TensorShapeProtos";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "org.tensorflow.framework";
|
||||
|
||||
package tensorflow;
|
||||
|
||||
// Dimensions of a tensor.
|
||||
message TensorShapeProto {
|
||||
// One dimension of the tensor.
|
||||
message Dim {
|
||||
// Size of the tensor in that dimension.
|
||||
// This value must be >= -1, but values of -1 are reserved for "unknown"
|
||||
// shapes (values of -1 mean "unknown" dimension). Certain wrappers
|
||||
// that work with TensorShapeProto may fail at runtime when deserializing
|
||||
// a TensorShapeProto containing a dim value of -1.
|
||||
int64 size = 1;
|
||||
|
||||
// Optional name of the tensor dimension.
|
||||
string name = 2;
|
||||
};
|
||||
|
||||
// Dimensions of the tensor, such as {"input", 30}, {"output", 40}
|
||||
// for a 30 x 40 2D tensor. If an entry has size -1, this
|
||||
// corresponds to a dimension of unknown size. The names are
|
||||
// optional.
|
||||
//
|
||||
// The order of entries in "dim" matters: It indicates the layout of the
|
||||
// values in the tensor in-memory representation.
|
||||
//
|
||||
// The first entry in "dim" is the outermost dimension used to layout the
|
||||
// values, the last entry is the innermost dimension. This matches the
|
||||
// in-memory layout of RowMajor Eigen tensors.
|
||||
//
|
||||
// If "dim.size()" > 0, "unknown_rank" must be false.
|
||||
repeated Dim dim = 2;
|
||||
|
||||
// If true, the number of dimensions in the shape is unknown.
|
||||
//
|
||||
// If true, "dim.size()" must be 0.
|
||||
bool unknown_rank = 3;
|
||||
};
|
||||
@@ -0,0 +1,759 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2016, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
/*
|
||||
Implementation of Tensorflow models parser
|
||||
*/
|
||||
|
||||
#include "../precomp.hpp"
|
||||
using namespace cv;
|
||||
using namespace cv::dnn;
|
||||
|
||||
#if HAVE_PROTOBUF
|
||||
#include "graph.pb.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <google/protobuf/message.h>
|
||||
#include <google/protobuf/text_format.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
#include "tf_io.hpp"
|
||||
|
||||
using ::google::protobuf::RepeatedField;
|
||||
using ::google::protobuf::RepeatedPtrField;
|
||||
using ::google::protobuf::Message;
|
||||
using ::google::protobuf::Descriptor;
|
||||
using ::google::protobuf::FieldDescriptor;
|
||||
using ::google::protobuf::Reflection;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
static int toNCHW[] = {0, 2, 3, 1};
|
||||
|
||||
typedef std::vector<std::pair<String, int> > StrIntVector;
|
||||
|
||||
struct Pin
|
||||
{
|
||||
Pin(const std::string &_name, int _blobIndex = 0) :
|
||||
name(_name), blobIndex(_blobIndex) {}
|
||||
|
||||
Pin() :
|
||||
name(""), blobIndex(-1) {}
|
||||
|
||||
std::string name;
|
||||
int blobIndex;
|
||||
};
|
||||
|
||||
void blobShapeFromTensor(const tensorflow::TensorProto &tensor, MatShape& shape)
|
||||
{
|
||||
shape.clear();
|
||||
if (tensor.has_tensor_shape())
|
||||
{
|
||||
const tensorflow::TensorShapeProto &_shape = tensor.tensor_shape();
|
||||
int i, n = _shape.dim_size();
|
||||
shape.resize(n);
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
shape[i] = (int)_shape.dim(i).size();
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::StsError, "Unknown shape of input tensor");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void parseTensor(const tensorflow::TensorProto &tensor, Mat &dstBlob)
|
||||
{
|
||||
MatShape shape;
|
||||
blobShapeFromTensor(tensor, shape);
|
||||
int dims = (int)shape.size();
|
||||
|
||||
if (dims == 4)
|
||||
{
|
||||
// REORDER blob NHWC to NCHW
|
||||
swap(shape[2], shape[3]); // NHCW
|
||||
swap(shape[1], shape[2]); // NCHW
|
||||
}
|
||||
|
||||
dstBlob.create(shape, CV_32F);
|
||||
|
||||
int size = tensor.tensor_content().size() / sizeof(T);
|
||||
CV_Assert(size == (int)dstBlob.total());
|
||||
|
||||
float *dstData = dstBlob.ptr<float>();
|
||||
const T *data = reinterpret_cast<const T*>(tensor.tensor_content().c_str());
|
||||
|
||||
if (dims == 4)
|
||||
{
|
||||
int num = shape[0], channels = shape[1], height = shape[2], width = shape[3];
|
||||
int total = num*channels*height*width;
|
||||
for(int i_n = 0; i_n < shape[0]; i_n++) {
|
||||
for(int i_c = 0; i_c < shape[1]; i_c++) {
|
||||
for(int i_h = 0; i_h < shape[2]; i_h++) {
|
||||
for(int i_w = 0; i_w < shape[3]; i_w++) {
|
||||
int dst_i = channels*height*width*i_n + height*width*i_c + width*i_h + i_w;
|
||||
int src_i = channels*height*width*i_n + i_c + channels*width*i_h + channels*i_w;
|
||||
|
||||
CV_Assert(dst_i < total);
|
||||
CV_Assert(src_i < total);
|
||||
|
||||
dstData[dst_i] = data[src_i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < size; i++)
|
||||
dstData[i] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
void blobFromTensor(const tensorflow::TensorProto &tensor, Mat &dstBlob)
|
||||
{
|
||||
switch (tensor.dtype()) {
|
||||
case tensorflow::DT_FLOAT:
|
||||
parseTensor<float>(tensor, dstBlob);
|
||||
break;
|
||||
case tensorflow::DT_DOUBLE:
|
||||
parseTensor<double>(tensor, dstBlob);
|
||||
break;
|
||||
default:
|
||||
CV_Error(Error::StsError, "Tensor's data type is not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void printList(const tensorflow::AttrValue::ListValue &val)
|
||||
{
|
||||
std::cout << "(";
|
||||
for (int i = 0; i < val.i_size(); i++)
|
||||
std::cout << " " << val.i(i);
|
||||
std::cout << " )";
|
||||
}
|
||||
|
||||
void printTensorShape(const tensorflow::TensorShapeProto &shape)
|
||||
{
|
||||
std::cout << "[ ";
|
||||
for (int d = 0; d < shape.dim_size(); d++)
|
||||
std::cout << shape.dim(d).name() <<
|
||||
":" << shape.dim(d).size() << " ";
|
||||
std::cout << "]";
|
||||
}
|
||||
|
||||
void printTensor(const tensorflow::TensorProto &tensor)
|
||||
{
|
||||
printTensorShape(tensor.tensor_shape());
|
||||
|
||||
if (tensor.tensor_content().empty())
|
||||
return;
|
||||
|
||||
switch (tensor.dtype())
|
||||
{
|
||||
case 1: // float
|
||||
{
|
||||
const float *data = reinterpret_cast<const float*>(tensor.tensor_content().c_str());
|
||||
int size = tensor.tensor_content().size() / sizeof(float);
|
||||
for (int i = 0; i < std::min(10, size); i++)
|
||||
std::cout << " " << data[i];
|
||||
if (size > 10)
|
||||
std::cout << " ... " << size - 10 << " more";
|
||||
break;
|
||||
}
|
||||
case 3: // int32
|
||||
{
|
||||
const int *data = reinterpret_cast<const int*>(tensor.tensor_content().c_str());
|
||||
int size = tensor.tensor_content().size() / sizeof(int);
|
||||
for (int i = 0; i < std::min(10, size); i++)
|
||||
std::cout << " " << data[i];
|
||||
if (size > 10)
|
||||
std::cout << " ... " << size - 10 << " more";
|
||||
break;
|
||||
}
|
||||
default:
|
||||
CV_Error(Error::StsError, "Tensor type is not supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void printLayerAttr(const tensorflow::NodeDef &layer)
|
||||
{
|
||||
std::cout << std::endl << layer.name() << ":" << layer.op();
|
||||
for (int ii = 0; ii < layer.input_size(); ii++)
|
||||
std::cout << "(" << layer.input(ii) << ")";
|
||||
std::cout << std::endl;
|
||||
google::protobuf::Map<std::string, tensorflow::AttrValue> attr
|
||||
= layer.attr();
|
||||
for (google::protobuf::Map<std::string, tensorflow::AttrValue>::const_iterator ai = attr.begin();
|
||||
ai != attr.end(); ++ai)
|
||||
{
|
||||
std::cout << ai->first << ":";
|
||||
if (ai->first == "dtype" || ai->first == "T")
|
||||
std::cout << ai->second.i();
|
||||
else if (ai->first == "padding")
|
||||
std::cout << ai->second.s();
|
||||
else if (ai->first == "transpose_a" || ai->first == "transpose_b")
|
||||
std::cout << ai->second.b();
|
||||
// else if (ai->first == "shape")
|
||||
// printTensorShape(ai->second.shape());
|
||||
else if (ai->first == "strides" || ai->first == "ksize")
|
||||
printList(ai->second.list());
|
||||
else
|
||||
printTensor(ai->second.tensor());
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
bool hasLayerAttr(const tensorflow::NodeDef &layer, const std::string &name)
|
||||
{
|
||||
google::protobuf::Map<std::string, tensorflow::AttrValue> attr = layer.attr();
|
||||
return attr.find(name) != attr.end();
|
||||
}
|
||||
|
||||
const tensorflow::AttrValue& getLayerAttr(const tensorflow::NodeDef &layer, const std::string &name)
|
||||
{
|
||||
return layer.attr().at(name);
|
||||
}
|
||||
|
||||
void setStrides(LayerParams &layerParams, const tensorflow::NodeDef &layer)
|
||||
{
|
||||
if (hasLayerAttr(layer, "strides"))
|
||||
{
|
||||
const tensorflow::AttrValue& val = getLayerAttr(layer, "strides");
|
||||
if (val.list().i_size() != 4 ||
|
||||
val.list().i(0) != 1 || val.list().i(3) != 1)
|
||||
CV_Error(Error::StsError, "Unsupported strides");
|
||||
layerParams.set("stride_h", static_cast<int>(val.list().i(1)));
|
||||
layerParams.set("stride_w", static_cast<int>(val.list().i(2)));
|
||||
}
|
||||
}
|
||||
|
||||
DictValue parseDims(const tensorflow::TensorProto &tensor) {
|
||||
MatShape shape;
|
||||
blobShapeFromTensor(tensor, shape);
|
||||
int dims = (int)shape.size();
|
||||
|
||||
CV_Assert(tensor.dtype() == tensorflow::DT_INT32);
|
||||
CV_Assert(dims == 1);
|
||||
|
||||
int size = tensor.tensor_content().size() / sizeof(int);
|
||||
const int *data = reinterpret_cast<const int*>(tensor.tensor_content().c_str());
|
||||
// TODO: add reordering shape if dims == 4
|
||||
return DictValue::arrayInt(data, size);
|
||||
}
|
||||
|
||||
void setKSize(LayerParams &layerParams, const tensorflow::NodeDef &layer)
|
||||
{
|
||||
if (hasLayerAttr(layer, "ksize"))
|
||||
{
|
||||
const tensorflow::AttrValue& val = getLayerAttr(layer, "ksize");
|
||||
if (val.list().i_size() != 4 ||
|
||||
val.list().i(0) != 1 || val.list().i(3) != 1)
|
||||
CV_Error(Error::StsError, "Unsupported ksize");
|
||||
layerParams.set("kernel_h", static_cast<int>(val.list().i(1)));
|
||||
layerParams.set("kernel_w", static_cast<int>(val.list().i(2)));
|
||||
}
|
||||
else
|
||||
{
|
||||
layerParams.set("kernel_h", 1);
|
||||
layerParams.set("kernel_w", 1);
|
||||
}
|
||||
}
|
||||
|
||||
void setPadding(LayerParams &layerParams, const tensorflow::NodeDef &layer)
|
||||
{
|
||||
if (hasLayerAttr(layer, "padding"))
|
||||
layerParams.set("pad_mode", getLayerAttr(layer, "padding").s());
|
||||
}
|
||||
|
||||
void RemoveIdentityOps(tensorflow::GraphDef& net) {
|
||||
typedef std::map<String, String> IdentityOpsMap;
|
||||
IdentityOpsMap identity_ops;
|
||||
|
||||
std::vector<int> identity_ops_idx;
|
||||
|
||||
int layersCount = net.node_size();
|
||||
for (int li = 0; li < layersCount; li++)
|
||||
{
|
||||
const tensorflow::NodeDef &layer = net.node(li);
|
||||
String type = layer.op();
|
||||
|
||||
if (type == "Identity") {
|
||||
identity_ops_idx.push_back(li);
|
||||
identity_ops[layer.name()] = layer.input(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (int li = 0; li < layersCount; li++)
|
||||
{
|
||||
tensorflow::NodeDef* layer = net.mutable_node(li);
|
||||
for (int input_id = 0; input_id < layer->input_size(); input_id++) {
|
||||
String input_op_name = layer->input(input_id);
|
||||
IdentityOpsMap::iterator it = identity_ops.find(input_op_name);
|
||||
|
||||
if (it != identity_ops.end()) {
|
||||
layer->set_input(input_id, it->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(identity_ops_idx.begin(), identity_ops_idx.end());
|
||||
|
||||
int removed_nodes = 0;
|
||||
for(size_t i = 0; i < identity_ops_idx.size(); i++) {
|
||||
int start_id = identity_ops_idx[i] - removed_nodes;
|
||||
net.mutable_node()->DeleteSubrange(start_id, 1);
|
||||
removed_nodes++;
|
||||
}
|
||||
}
|
||||
|
||||
Pin parsePin(const std::string &name)
|
||||
{
|
||||
Pin pin(name);
|
||||
|
||||
size_t delimiter_pos = name.find_first_of(":");
|
||||
if (delimiter_pos != std::string::npos)
|
||||
{
|
||||
pin.name = name.substr(0, delimiter_pos);
|
||||
std::istringstream(name.substr(delimiter_pos + 1)) >> pin.blobIndex;
|
||||
}
|
||||
|
||||
return pin;
|
||||
}
|
||||
|
||||
StrIntVector getNextLayers(const tensorflow::GraphDef& net, const String& layer_name, const String& type = "")
|
||||
{
|
||||
StrIntVector layers;
|
||||
|
||||
for (int li = 0; li < net.node_size(); li++)
|
||||
{
|
||||
const tensorflow::NodeDef& layer = net.node(li);
|
||||
for (int input_id = 0; input_id < layer.input_size(); input_id++) {
|
||||
String input_op_name = parsePin(layer.input(input_id)).name;
|
||||
bool type_ok = type.empty() ? true : type == layer.op();
|
||||
if (input_op_name == layer_name && type_ok)
|
||||
layers.push_back(std::make_pair(layer.name(), li));
|
||||
}
|
||||
}
|
||||
|
||||
return layers;
|
||||
}
|
||||
|
||||
void ExcludeLayer(tensorflow::GraphDef& net, const int layer_index, const int input_blob_index, bool remove_from_net = true) {
|
||||
String layer_name = net.node(layer_index).name();
|
||||
StrIntVector layers = getNextLayers(net, layer_name);
|
||||
|
||||
String removed_layer_input = net.node(layer_index).input(input_blob_index);
|
||||
|
||||
for (size_t i = 0; i < layers.size(); i++)
|
||||
{
|
||||
tensorflow::NodeDef* layer = net.mutable_node(layers[i].second);
|
||||
for (int input_id = 0; input_id < layer->input_size(); input_id++) {
|
||||
String input_op_name = layer->input(input_id);
|
||||
|
||||
if (input_op_name == layer_name) {
|
||||
layer->set_input(input_id, removed_layer_input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (remove_from_net)
|
||||
net.mutable_node()->DeleteSubrange(layer_index, 1);
|
||||
}
|
||||
|
||||
class TFImporter : public Importer {
|
||||
public:
|
||||
TFImporter(const char *model);
|
||||
void populateNet(Net dstNet);
|
||||
~TFImporter() {}
|
||||
|
||||
private:
|
||||
void kernelFromTensor(const tensorflow::TensorProto &tensor, Mat &dstBlob);
|
||||
|
||||
void connect(const std::map<String, int>& layers_name_id_map, Net& network, const Pin& outPin,
|
||||
const int input_layer_id, const int input_blob_id);
|
||||
void connectToAllBlobs(const std::map<String, int>& layer_id, Net& network, const Pin& outPin,
|
||||
const int input_layer_id, const int input_blobs_count);
|
||||
const tensorflow::TensorProto& getConstBlob(const tensorflow::NodeDef &layer, std::map<String, int> const_layers,
|
||||
int input_blob_index = -1, int* actual_inp_blob_idx = 0);
|
||||
|
||||
|
||||
tensorflow::GraphDef net;
|
||||
};
|
||||
|
||||
TFImporter::TFImporter(const char *model)
|
||||
{
|
||||
if (model && model[0])
|
||||
ReadTFNetParamsFromBinaryFileOrDie(model, &net);
|
||||
}
|
||||
|
||||
void TFImporter::kernelFromTensor(const tensorflow::TensorProto &tensor, Mat &dstBlob)
|
||||
{
|
||||
MatShape shape;
|
||||
blobShapeFromTensor(tensor, shape);
|
||||
int dims = (int)shape.size();
|
||||
|
||||
// TODO: other blob types
|
||||
CV_Assert(tensor.dtype() == tensorflow::DT_FLOAT);
|
||||
CV_Assert(dims == 4);
|
||||
|
||||
// REORDER kernel HWIO to OIHW
|
||||
swap(shape[0], shape[2]); // IWHO
|
||||
swap(shape[1], shape[3]); // IOHW
|
||||
swap(shape[0], shape[1]); // OIHW
|
||||
|
||||
dstBlob.create(shape, CV_32F);
|
||||
|
||||
int size = tensor.tensor_content().size() / sizeof(float);
|
||||
CV_Assert(size == (int)dstBlob.total());
|
||||
|
||||
float *dstData = dstBlob.ptr<float>();
|
||||
const float *data = reinterpret_cast<const float*>(tensor.tensor_content().c_str());
|
||||
|
||||
int out_c = shape[0], input_c = shape[1], height = shape[2], width = shape[3];
|
||||
int total = out_c*input_c*height*width;
|
||||
for(int i_oc = 0; i_oc < out_c; i_oc++) {
|
||||
for(int i_ic = 0; i_ic < input_c; i_ic++) {
|
||||
for(int i_h = 0; i_h < height; i_h++) {
|
||||
for(int i_w = 0; i_w < width; i_w++) {
|
||||
int dst_i = input_c*height*width*i_oc + height*width*i_ic + width*i_h + i_w;
|
||||
int src_i = out_c*input_c*width*i_h + out_c*input_c*i_w + out_c*i_ic + i_oc;
|
||||
CV_Assert(dst_i < total);
|
||||
CV_Assert(src_i < total);
|
||||
dstData[dst_i] = data[src_i];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TFImporter::connect(const std::map<String, int>& layers_name_id_map, Net& network, const Pin& outPin,
|
||||
const int input_layer_id, const int input_blob_id)
|
||||
{
|
||||
std::map<String, int>::const_iterator it = layers_name_id_map.find(outPin.name);
|
||||
if (it == layers_name_id_map.end())
|
||||
CV_Error(Error::StsError, "Input layer not found: " + outPin.name);
|
||||
network.connect(it->second, outPin.blobIndex, input_layer_id, input_blob_id);
|
||||
}
|
||||
|
||||
void TFImporter::connectToAllBlobs(const std::map<String, int>& layer_id, Net& network, const Pin& outPin,
|
||||
const int input_layer_id, const int input_blobs_count)
|
||||
{
|
||||
for (int input_blob_id = 0; input_blob_id < input_blobs_count; input_blob_id++)
|
||||
connect(layer_id, network, outPin, input_layer_id, input_blob_id);
|
||||
}
|
||||
|
||||
const tensorflow::TensorProto& TFImporter::getConstBlob(const tensorflow::NodeDef &layer, std::map<String, int> const_layers,
|
||||
int input_blob_index, int* actual_inp_blob_idx) {
|
||||
if (input_blob_index == -1) {
|
||||
for(int i = 0; i < layer.input_size(); i++) {
|
||||
Pin input = parsePin(layer.input(i));
|
||||
if (const_layers.find(input.name) != const_layers.end()) {
|
||||
if (input_blob_index != -1)
|
||||
CV_Error(Error::StsError, "More than one input is Const op");
|
||||
|
||||
input_blob_index = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (input_blob_index == -1)
|
||||
CV_Error(Error::StsError, "Const input blob for weights not found");
|
||||
|
||||
Pin kernel_inp = parsePin(layer.input(input_blob_index));
|
||||
if (const_layers.find(kernel_inp.name) == const_layers.end())
|
||||
CV_Error(Error::StsError, "Const kernel input not found");
|
||||
if (kernel_inp.blobIndex != 0)
|
||||
CV_Error(Error::StsError, "Unsupported kernel input");
|
||||
|
||||
if(actual_inp_blob_idx) {
|
||||
*actual_inp_blob_idx = input_blob_index;
|
||||
}
|
||||
|
||||
return net.node(const_layers.at(kernel_inp.name)).attr().at("value").tensor();
|
||||
}
|
||||
|
||||
|
||||
void TFImporter::populateNet(Net dstNet)
|
||||
{
|
||||
RemoveIdentityOps(net);
|
||||
|
||||
std::map<int, String> layers_to_ignore;
|
||||
|
||||
int layersSize = net.node_size();
|
||||
|
||||
// find all Const layers for params
|
||||
std::map<String, int> value_id;
|
||||
for (int li = 0; li < layersSize; li++)
|
||||
{
|
||||
const tensorflow::NodeDef &layer = net.node(li);
|
||||
String name = layer.name();
|
||||
String type = layer.op();
|
||||
|
||||
if (type != "Const")
|
||||
continue; // only Const parameters are supported
|
||||
|
||||
if (layer.attr().find("value") != layer.attr().end())
|
||||
{
|
||||
value_id.insert(std::make_pair(name, li));
|
||||
}
|
||||
|
||||
layers_to_ignore[li] = name;
|
||||
}
|
||||
|
||||
std::map<String, int> layer_id;
|
||||
|
||||
for (int li = 0; li < layersSize; li++)
|
||||
{
|
||||
const tensorflow::NodeDef &layer = net.node(li);
|
||||
String name = layer.name();
|
||||
String type = layer.op();
|
||||
LayerParams layerParams;
|
||||
|
||||
if(layers_to_ignore.find(li) != layers_to_ignore.end())
|
||||
continue;
|
||||
|
||||
if (type == "Conv2D")
|
||||
{
|
||||
layerParams.set("bias_term", false);
|
||||
layerParams.blobs.resize(1);
|
||||
|
||||
StrIntVector next_layers = getNextLayers(net, name, "BiasAdd");
|
||||
if (next_layers.size() == 1) {
|
||||
layerParams.set("bias_term", true);
|
||||
layerParams.blobs.resize(2);
|
||||
|
||||
int weights_layer_index = next_layers[0].second;
|
||||
|
||||
blobFromTensor(getConstBlob(net.node(weights_layer_index), value_id), layerParams.blobs[1]);
|
||||
ExcludeLayer(net, weights_layer_index, 0, false);
|
||||
layers_to_ignore[weights_layer_index] = next_layers[0].first;
|
||||
}
|
||||
|
||||
kernelFromTensor(getConstBlob(layer, value_id), layerParams.blobs[0]);
|
||||
const int* kshape = layerParams.blobs[0].size.p;
|
||||
layerParams.set("kernel_h", kshape[2]);
|
||||
layerParams.set("kernel_w", kshape[3]);
|
||||
layerParams.set("num_output", kshape[0]);
|
||||
|
||||
setStrides(layerParams, layer);
|
||||
setPadding(layerParams, layer);
|
||||
|
||||
int id = dstNet.addLayer(name, "Convolution", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
// one input only
|
||||
connect(layer_id, dstNet, parsePin(layer.input(0)), id, 0);
|
||||
}
|
||||
else if (type == "BiasAdd" || type == "Add")
|
||||
{
|
||||
layerParams.blobs.resize(1);
|
||||
blobFromTensor(getConstBlob(layer, value_id), layerParams.blobs[0]);
|
||||
|
||||
int id = dstNet.addLayer(name, "Shift", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
// one input only
|
||||
connect(layer_id, dstNet, parsePin(layer.input(0)), id, 0);
|
||||
}
|
||||
else if (type == "Identity")
|
||||
{
|
||||
int id = dstNet.addLayer(name, "Identity", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
connectToAllBlobs(layer_id, dstNet, parsePin(layer.input(0)), id, layer.input_size());
|
||||
}
|
||||
else if (type == "MatMul")
|
||||
{
|
||||
CV_Assert(layer.input_size() == 2);
|
||||
|
||||
layerParams.set("bias_term", false);
|
||||
layerParams.blobs.resize(1);
|
||||
|
||||
StrIntVector next_layers = getNextLayers(net, name, "BiasAdd");
|
||||
if (next_layers.size() == 1) {
|
||||
layerParams.set("bias_term", true);
|
||||
layerParams.blobs.resize(2);
|
||||
|
||||
int weights_layer_index = next_layers[0].second;
|
||||
blobFromTensor(getConstBlob(net.node(weights_layer_index), value_id), layerParams.blobs[1]);
|
||||
ExcludeLayer(net, weights_layer_index, 0, false);
|
||||
layers_to_ignore[weights_layer_index] = next_layers[0].first;
|
||||
}
|
||||
|
||||
int kernel_blob_index = -1;
|
||||
blobFromTensor(getConstBlob(layer, value_id, -1, &kernel_blob_index), layerParams.blobs[0]);
|
||||
|
||||
if (kernel_blob_index == 1) { // In this case output is computed by x*W formula - W should be transposed
|
||||
Mat data = layerParams.blobs[0].t();
|
||||
layerParams.blobs[0] = data.clone();
|
||||
}
|
||||
|
||||
layerParams.set("num_output", layerParams.blobs[0].size[0]);
|
||||
|
||||
int id = dstNet.addLayer(name, "InnerProduct", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
// one input only
|
||||
int input_blob_index = kernel_blob_index == 0 ? 1 : 0;
|
||||
connect(layer_id, dstNet, parsePin(layer.input(input_blob_index)), id, 0);
|
||||
}
|
||||
else if (type == "Reshape")
|
||||
{
|
||||
layerParams.set("dim", parseDims(getConstBlob(layer, value_id, 1)));
|
||||
layerParams.set("reorder_dims", true);
|
||||
|
||||
int id = dstNet.addLayer(name, "Reshape", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
// one input only
|
||||
connect(layer_id, dstNet, parsePin(layer.input(0)), id, 0);
|
||||
}
|
||||
else if (type == "Const")
|
||||
{
|
||||
}
|
||||
else if (type == "Softmax")
|
||||
{
|
||||
int id = dstNet.addLayer(name, "Softmax", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
connectToAllBlobs(layer_id, dstNet, parsePin(layer.input(0)), id, layer.input_size());
|
||||
}
|
||||
else if (type == "LRN")
|
||||
{
|
||||
if(hasLayerAttr(layer, "alpha")) {
|
||||
layerParams.set("alpha", getLayerAttr(layer, "alpha").f());
|
||||
}
|
||||
if(hasLayerAttr(layer, "beta")) {
|
||||
layerParams.set("beta", getLayerAttr(layer, "beta").f());
|
||||
}
|
||||
if(hasLayerAttr(layer, "depth_radius")) {
|
||||
int radius = (int)getLayerAttr(layer, "depth_radius").i();
|
||||
layerParams.set("local_size", 2*radius + 1);
|
||||
}
|
||||
if(hasLayerAttr(layer, "bias")) {
|
||||
layerParams.set("bias", getLayerAttr(layer, "bias").f());
|
||||
}
|
||||
layerParams.set("norm_by_size", false);
|
||||
|
||||
int id = dstNet.addLayer(name, "LRN", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
connectToAllBlobs(layer_id, dstNet, parsePin(layer.input(0)), id, layer.input_size());
|
||||
}
|
||||
else if (type == "Concat")
|
||||
{
|
||||
int axis = getConstBlob(layer, value_id, 0).int_val().Get(0);
|
||||
layerParams.set("axis", toNCHW[axis]);
|
||||
|
||||
int id = dstNet.addLayer(name, "Concat", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
// input(0) is concat_dim
|
||||
for (int ii = 1; ii < layer.input_size(); ii++)
|
||||
{
|
||||
Pin inp = parsePin(layer.input(ii));
|
||||
if (layer_id.find(inp.name) == layer_id.end())
|
||||
CV_Error(Error::StsError, "Input layer not found: " + inp.name);
|
||||
dstNet.connect(layer_id.at(inp.name), inp.blobIndex, id, ii - 1);
|
||||
}
|
||||
}
|
||||
else if (type == "Relu")
|
||||
{
|
||||
int id = dstNet.addLayer(name, "ReLU", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
connectToAllBlobs(layer_id, dstNet, parsePin(layer.input(0)), id, layer.input_size());
|
||||
}
|
||||
else if (type == "MaxPool")
|
||||
{
|
||||
layerParams.set("pool", "max");
|
||||
|
||||
setKSize(layerParams, layer);
|
||||
setStrides(layerParams, layer);
|
||||
setPadding(layerParams, layer);
|
||||
|
||||
int id = dstNet.addLayer(name, "Pooling", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
connectToAllBlobs(layer_id, dstNet, parsePin(layer.input(0)), id, layer.input_size());
|
||||
}
|
||||
else if (type == "AvgPool")
|
||||
{
|
||||
layerParams.set("pool", "ave");
|
||||
|
||||
setKSize(layerParams, layer);
|
||||
setStrides(layerParams, layer);
|
||||
setPadding(layerParams, layer);
|
||||
|
||||
int id = dstNet.addLayer(name, "Pooling", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
connectToAllBlobs(layer_id, dstNet, parsePin(layer.input(0)), id, layer.input_size());
|
||||
}
|
||||
else if (type == "Placeholder")
|
||||
{
|
||||
std::vector<String> netInputs(1);
|
||||
netInputs[0] = name;
|
||||
layer_id[name] = 0;
|
||||
dstNet.setInputsNames(netInputs);
|
||||
}
|
||||
else if (type == "Split") {
|
||||
// TODO: determing axis index remapping by input dimensions order of input blob
|
||||
// TODO: slicing input may be Const op
|
||||
// TODO: slicing kernels for convolutions - in current implenmentation it is impossible
|
||||
// TODO: add parsing num of slices parameter
|
||||
CV_Assert(layer.input_size() == 2);
|
||||
// num_split
|
||||
// 1st blob is dims tensor
|
||||
layerParams.set("slice_point", DictValue::arrayReal((double*)0, 0));
|
||||
|
||||
int axis = getConstBlob(layer, value_id, 0).int_val().Get(0);
|
||||
layerParams.set("axis", toNCHW[axis]);
|
||||
|
||||
int id = dstNet.addLayer(name, "Slice", layerParams);
|
||||
layer_id[name] = id;
|
||||
|
||||
// one input only
|
||||
connect(layer_id, dstNet, parsePin(layer.input(1)), id, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
printLayerAttr(layer);
|
||||
CV_Error_(Error::StsError, ("Unknown layer type %s in op %s", type.c_str(), name.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Ptr<Importer> cv::dnn::createTensorflowImporter(const String &model)
|
||||
{
|
||||
return Ptr<Importer>(new TFImporter(model.c_str()));
|
||||
}
|
||||
|
||||
#else //HAVE_PROTOBUF
|
||||
|
||||
Ptr<Importer> cv::dnn::createTensorflowImporter(const String&)
|
||||
{
|
||||
CV_Error(cv::Error::StsNotImplemented, "libprotobuf required to import data from TensorFlow models");
|
||||
return Ptr<Importer>();
|
||||
}
|
||||
|
||||
#endif //HAVE_PROTOBUF
|
||||
|
||||
Net cv::dnn::readNetFromTensorflow(const String &model)
|
||||
{
|
||||
Ptr<Importer> importer = createTensorflowImporter(model);
|
||||
Net net;
|
||||
if (importer)
|
||||
importer->populateNet(net);
|
||||
return net;
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2016, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
/*
|
||||
Implementation of various functions which are related to Tensorflow models reading.
|
||||
*/
|
||||
|
||||
#if HAVE_PROTOBUF
|
||||
#include <google/protobuf/io/coded_stream.h>
|
||||
#include <google/protobuf/io/zero_copy_stream_impl.h>
|
||||
#include <google/protobuf/text_format.h>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
#include "graph.pb.h"
|
||||
#include "tf_io.hpp"
|
||||
#include "../caffe/glog_emulator.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace dnn {
|
||||
|
||||
using std::string;
|
||||
using std::map;
|
||||
using namespace tensorflow;
|
||||
using namespace ::google::protobuf;
|
||||
using namespace ::google::protobuf::io;
|
||||
|
||||
const int kProtoReadBytesLimit = INT_MAX; // Max size of 2 GB minus 1 byte.
|
||||
|
||||
// TODO: remove Caffe duplicate
|
||||
bool ReadProtoFromBinaryFileTF(const char* filename, Message* proto) {
|
||||
std::ifstream fs(filename, std::ifstream::in | std::ifstream::binary);
|
||||
CHECK(fs.is_open()) << "Can't open \"" << filename << "\"";
|
||||
ZeroCopyInputStream* raw_input = new IstreamInputStream(&fs);
|
||||
CodedInputStream* coded_input = new CodedInputStream(raw_input);
|
||||
coded_input->SetTotalBytesLimit(kProtoReadBytesLimit, 536870912);
|
||||
|
||||
bool success = proto->ParseFromCodedStream(coded_input);
|
||||
|
||||
delete coded_input;
|
||||
delete raw_input;
|
||||
fs.close();
|
||||
return success;
|
||||
}
|
||||
|
||||
void ReadTFNetParamsFromBinaryFileOrDie(const char* param_file,
|
||||
tensorflow::GraphDef* param) {
|
||||
CHECK(ReadProtoFromBinaryFileTF(param_file, param))
|
||||
<< "Failed to parse GraphDef file: " << param_file;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,29 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
|
||||
// Copyright (C) 2016, Intel Corporation, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
/*
|
||||
Declaration of various functions which are related to Tensorflow models reading.
|
||||
*/
|
||||
|
||||
#ifndef __OPENCV_DNN_TF_IO_HPP__
|
||||
#define __OPENCV_DNN_TF_IO_HPP__
|
||||
#if HAVE_PROTOBUF
|
||||
|
||||
#include "graph.pb.h"
|
||||
|
||||
namespace cv {
|
||||
namespace dnn {
|
||||
|
||||
// Read parameters from a file into a GraphDef proto message.
|
||||
void ReadTFNetParamsFromBinaryFileOrDie(const char* param_file,
|
||||
tensorflow::GraphDef* param);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,60 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package tensorflow;
|
||||
option cc_enable_arenas = true;
|
||||
option java_outer_classname = "TypesProtos";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "org.tensorflow.framework";
|
||||
|
||||
enum DataType {
|
||||
// Not a legal value for DataType. Used to indicate a DataType field
|
||||
// has not been set.
|
||||
DT_INVALID = 0;
|
||||
|
||||
// Data types that all computation devices are expected to be
|
||||
// capable to support.
|
||||
DT_FLOAT = 1;
|
||||
DT_DOUBLE = 2;
|
||||
DT_INT32 = 3;
|
||||
DT_UINT8 = 4;
|
||||
DT_INT16 = 5;
|
||||
DT_INT8 = 6;
|
||||
DT_STRING = 7;
|
||||
DT_COMPLEX64 = 8; // Single-precision complex
|
||||
DT_INT64 = 9;
|
||||
DT_BOOL = 10;
|
||||
DT_QINT8 = 11; // Quantized int8
|
||||
DT_QUINT8 = 12; // Quantized uint8
|
||||
DT_QINT32 = 13; // Quantized int32
|
||||
DT_BFLOAT16 = 14; // Float32 truncated to 16 bits. Only for cast ops.
|
||||
DT_QINT16 = 15; // Quantized int16
|
||||
DT_QUINT16 = 16; // Quantized uint16
|
||||
DT_UINT16 = 17;
|
||||
DT_COMPLEX128 = 18; // Double-precision complex
|
||||
DT_HALF = 19;
|
||||
|
||||
// TODO(josh11b): DT_GENERIC_PROTO = ??;
|
||||
// TODO(jeff,josh11b): DT_UINT64? DT_UINT32?
|
||||
|
||||
// Do not use! These are only for parameters. Every enum above
|
||||
// should have a corresponding value below (verified by types_test).
|
||||
DT_FLOAT_REF = 101;
|
||||
DT_DOUBLE_REF = 102;
|
||||
DT_INT32_REF = 103;
|
||||
DT_UINT8_REF = 104;
|
||||
DT_INT16_REF = 105;
|
||||
DT_INT8_REF = 106;
|
||||
DT_STRING_REF = 107;
|
||||
DT_COMPLEX64_REF = 108;
|
||||
DT_INT64_REF = 109;
|
||||
DT_BOOL_REF = 110;
|
||||
DT_QINT8_REF = 111;
|
||||
DT_QUINT8_REF = 112;
|
||||
DT_QINT32_REF = 113;
|
||||
DT_BFLOAT16_REF = 114;
|
||||
DT_QINT16_REF = 115;
|
||||
DT_QUINT16_REF = 116;
|
||||
DT_UINT16_REF = 117;
|
||||
DT_COMPLEX128_REF = 118;
|
||||
DT_HALF_REF = 119;
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package tensorflow;
|
||||
option cc_enable_arenas = true;
|
||||
option java_outer_classname = "VersionsProtos";
|
||||
option java_multiple_files = true;
|
||||
option java_package = "org.tensorflow.framework";
|
||||
|
||||
// Version information for a piece of serialized data
|
||||
//
|
||||
// There are different types of versions for each type of data
|
||||
// (GraphDef, etc.), but they all have the same common shape
|
||||
// described here.
|
||||
//
|
||||
// Each consumer has "consumer" and "min_producer" versions (specified
|
||||
// elsewhere). A consumer is allowed to consume this data if
|
||||
//
|
||||
// producer >= min_producer
|
||||
// consumer >= min_consumer
|
||||
// consumer not in bad_consumers
|
||||
//
|
||||
message VersionDef {
|
||||
// The version of the code that produced this data.
|
||||
int32 producer = 1;
|
||||
|
||||
// Any consumer below this version is not allowed to consume this data.
|
||||
int32 min_consumer = 2;
|
||||
|
||||
// Specific consumer versions which are disallowed (e.g. due to bugs).
|
||||
repeated int32 bad_consumers = 3;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
|
||||
Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu)
|
||||
Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
|
||||
Copyright (c) 2011-2013 NYU (Clement Farabet)
|
||||
Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)
|
||||
Copyright (c) 2006 Idiap Research Institute (Samy Bengio)
|
||||
Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the names of Deepmind Technologies, NYU, NEC Laboratories America
|
||||
and IDIAP Research Institute nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,798 @@
|
||||
#if defined(ENABLE_TORCH_IMPORTER) && ENABLE_TORCH_IMPORTER
|
||||
#include "THGeneral.h"
|
||||
#include "THDiskFile.h"
|
||||
#include "THFilePrivate.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
typedef struct THDiskFile__
|
||||
{
|
||||
THFile file;
|
||||
|
||||
FILE *handle;
|
||||
char *name;
|
||||
int isNativeEncoding;
|
||||
int longSize;
|
||||
|
||||
} THDiskFile;
|
||||
|
||||
static int THDiskFile_isOpened(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)self;
|
||||
return (dfself->handle != NULL);
|
||||
}
|
||||
|
||||
const char *THDiskFile_name(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)self;
|
||||
return dfself->name;
|
||||
}
|
||||
|
||||
/* workaround mac osx lion ***insane*** fread bug */
|
||||
#ifdef __APPLE__
|
||||
static size_t fread__(void *ptr, size_t size, size_t nitems, FILE *stream)
|
||||
{
|
||||
size_t nread = 0;
|
||||
while(!feof(stream) && !ferror(stream) && (nread < nitems))
|
||||
nread += fread((char*)ptr+nread*size, size, THMin(2147483648UL/size, nitems-nread), stream);
|
||||
return nread;
|
||||
}
|
||||
#else
|
||||
#define fread__ fread
|
||||
#endif
|
||||
|
||||
#define READ_WRITE_METHODS(TYPE, TYPEC, ASCII_READ_ELEM, ASCII_WRITE_ELEM) \
|
||||
static long THDiskFile_read##TYPEC(THFile *self, TYPE *data, long n) \
|
||||
{ \
|
||||
THDiskFile *dfself = (THDiskFile*)(self); \
|
||||
long nread = 0L; \
|
||||
\
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file"); \
|
||||
THArgCheck(dfself->file.isReadable, 1, "attempt to read in a write-only file"); \
|
||||
\
|
||||
if(dfself->file.isBinary) \
|
||||
{ \
|
||||
nread = fread__(data, sizeof(TYPE), n, dfself->handle); \
|
||||
if(!dfself->isNativeEncoding && (sizeof(TYPE) > 1) && (nread > 0)) \
|
||||
THDiskFile_reverseMemory(data, data, sizeof(TYPE), nread); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
long i; \
|
||||
for(i = 0; i < n; i++) \
|
||||
{ \
|
||||
ASCII_READ_ELEM; /* increment here result and break if wrong */ \
|
||||
} \
|
||||
if(dfself->file.isAutoSpacing && (n > 0)) \
|
||||
{ \
|
||||
int c = fgetc(dfself->handle); \
|
||||
if( (c != '\n') && (c != EOF) ) \
|
||||
ungetc(c, dfself->handle); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
if(nread != n) \
|
||||
{ \
|
||||
dfself->file.hasError = 1; /* shouldn't we put hasError to 0 all the time ? */ \
|
||||
if(!dfself->file.isQuiet) \
|
||||
THError("read error: read %d blocks instead of %d", nread, n); \
|
||||
} \
|
||||
\
|
||||
return nread; \
|
||||
} \
|
||||
\
|
||||
static long THDiskFile_write##TYPEC(THFile *self, TYPE *data, long n) \
|
||||
{ \
|
||||
THDiskFile *dfself = (THDiskFile*)(self); \
|
||||
long nwrite = 0L; \
|
||||
\
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file"); \
|
||||
THArgCheck(dfself->file.isWritable, 1, "attempt to write in a read-only file"); \
|
||||
\
|
||||
if(dfself->file.isBinary) \
|
||||
{ \
|
||||
if(dfself->isNativeEncoding) \
|
||||
{ \
|
||||
nwrite = fwrite(data, sizeof(TYPE), n, dfself->handle); \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
if(sizeof(TYPE) > 1) \
|
||||
{ \
|
||||
char *buffer = (char*)THAlloc(sizeof(TYPE)*n); \
|
||||
THDiskFile_reverseMemory(buffer, data, sizeof(TYPE), n); \
|
||||
nwrite = fwrite(buffer, sizeof(TYPE), n, dfself->handle); \
|
||||
THFree(buffer); \
|
||||
} \
|
||||
else \
|
||||
nwrite = fwrite(data, sizeof(TYPE), n, dfself->handle); \
|
||||
} \
|
||||
} \
|
||||
else \
|
||||
{ \
|
||||
long i; \
|
||||
for(i = 0; i < n; i++) \
|
||||
{ \
|
||||
ASCII_WRITE_ELEM; \
|
||||
if( dfself->file.isAutoSpacing && (i < n-1) ) \
|
||||
fprintf(dfself->handle, " "); \
|
||||
} \
|
||||
if(dfself->file.isAutoSpacing && (n > 0)) \
|
||||
fprintf(dfself->handle, "\n"); \
|
||||
} \
|
||||
\
|
||||
if(nwrite != n) \
|
||||
{ \
|
||||
dfself->file.hasError = 1; \
|
||||
if(!dfself->file.isQuiet) \
|
||||
THError("write error: wrote %d blocks instead of %d", nwrite, n); \
|
||||
} \
|
||||
\
|
||||
return nwrite; \
|
||||
}
|
||||
|
||||
static int THDiskFile_mode(const char *mode, int *isReadable, int *isWritable)
|
||||
{
|
||||
*isReadable = 0;
|
||||
*isWritable = 0;
|
||||
if(strlen(mode) == 1)
|
||||
{
|
||||
if(*mode == 'r')
|
||||
{
|
||||
*isReadable = 1;
|
||||
return 1;
|
||||
}
|
||||
else if(*mode == 'w')
|
||||
{
|
||||
*isWritable = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
else if(strlen(mode) == 2)
|
||||
{
|
||||
if(mode[0] == 'r' && mode[1] == 'w')
|
||||
{
|
||||
*isReadable = 1;
|
||||
*isWritable = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void THDiskFile_synchronize(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
fflush(dfself->handle);
|
||||
}
|
||||
|
||||
static void THDiskFile_seek(THFile *self, long position)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
|
||||
#if defined(_WIN64)
|
||||
THArgCheck(position <= (_int64)INT64_MAX, 2, "position must be smaller than INT64_MAX");
|
||||
if(_fseeki64(dfself->handle, (__int64)position, SEEK_SET) < 0)
|
||||
#elif defined(_WIN32)
|
||||
THArgCheck(position <= (long)LONG_MAX, 2, "position must be smaller than LONG_MAX");
|
||||
if(fseek(dfself->handle, (long)position, SEEK_SET) < 0)
|
||||
#else
|
||||
THArgCheck(position <= (long)LLONG_MAX, 2, "position must be smaller than LLONG_MAX");
|
||||
if(fseeko(dfself->handle, (off_t)position, SEEK_SET) < 0)
|
||||
#endif
|
||||
{
|
||||
dfself->file.hasError = 1;
|
||||
if(!dfself->file.isQuiet)
|
||||
THError("unable to seek at position %d", position);
|
||||
}
|
||||
}
|
||||
|
||||
static void THDiskFile_seekEnd(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
|
||||
#if defined(_WIN64)
|
||||
if(_fseeki64(dfself->handle, 0L, SEEK_END) < 0)
|
||||
#elif defined(_WIN32)
|
||||
if(fseek(dfself->handle, 0L, SEEK_END) < 0)
|
||||
#else
|
||||
if(fseeko(dfself->handle, 0L, SEEK_END) < 0)
|
||||
#endif
|
||||
{
|
||||
dfself->file.hasError = 1;
|
||||
if(!dfself->file.isQuiet)
|
||||
THError("unable to seek at end of file");
|
||||
}
|
||||
}
|
||||
|
||||
static long THDiskFile_position(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
|
||||
#if defined(_WIN64)
|
||||
__int64 offset = _ftelli64(dfself->handle);
|
||||
#elif defined(_WIN32)
|
||||
long offset = ftell(dfself->handle);
|
||||
#else
|
||||
off_t offset = ftello(dfself->handle);
|
||||
#endif
|
||||
if (offset > -1)
|
||||
return (long)offset;
|
||||
else if(!dfself->file.isQuiet)
|
||||
THError("unable to obtain disk file offset (maybe a long overflow occurred)");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void THDiskFile_close(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
fclose(dfself->handle);
|
||||
dfself->handle = NULL;
|
||||
}
|
||||
|
||||
/* Little and Big Endian */
|
||||
|
||||
static void THDiskFile_reverseMemory(void *dst, const void *src, long blockSize, long numBlocks)
|
||||
{
|
||||
if(blockSize != 1)
|
||||
{
|
||||
long halfBlockSize = blockSize/2;
|
||||
char *charSrc = (char*)src;
|
||||
char *charDst = (char*)dst;
|
||||
long b, i;
|
||||
for(b = 0; b < numBlocks; b++)
|
||||
{
|
||||
for(i = 0; i < halfBlockSize; i++)
|
||||
{
|
||||
char z = charSrc[i];
|
||||
charDst[i] = charSrc[blockSize-1-i];
|
||||
charDst[blockSize-1-i] = z;
|
||||
}
|
||||
charSrc += blockSize;
|
||||
charDst += blockSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int THDiskFile_isLittleEndianCPU(void)
|
||||
{
|
||||
int x = 7;
|
||||
char *ptr = (char *)&x;
|
||||
|
||||
if(ptr[0] == 0)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
int THDiskFile_isBigEndianCPU(void)
|
||||
{
|
||||
return(!THDiskFile_isLittleEndianCPU());
|
||||
}
|
||||
|
||||
void THDiskFile_nativeEndianEncoding(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
dfself->isNativeEncoding = 1;
|
||||
}
|
||||
|
||||
void THDiskFile_littleEndianEncoding(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
dfself->isNativeEncoding = THDiskFile_isLittleEndianCPU();
|
||||
}
|
||||
|
||||
void THDiskFile_bigEndianEncoding(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
dfself->isNativeEncoding = !THDiskFile_isLittleEndianCPU();
|
||||
}
|
||||
|
||||
/* End of Little and Big Endian Stuff */
|
||||
|
||||
void THDiskFile_longSize(THFile *self, int size)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
THArgCheck(size == 0 || size == 4 || size == 8, 1, "Invalid long size specified");
|
||||
dfself->longSize = size;
|
||||
}
|
||||
|
||||
void THDiskFile_noBuffer(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
if (setvbuf(dfself->handle, NULL, _IONBF, 0)) {
|
||||
THError("error: cannot disable buffer");
|
||||
}
|
||||
}
|
||||
|
||||
static void THDiskFile_free(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
if(dfself->handle)
|
||||
fclose(dfself->handle);
|
||||
THFree(dfself->name);
|
||||
THFree(dfself);
|
||||
}
|
||||
|
||||
/* READ_WRITE_METHODS(int, Bool, */
|
||||
/* int value = 0; int ret = fscanf(file->handle, "%d", &value); array[i] = (value ? 1 : 0); if(ret <= 0) break; else result++, */
|
||||
/* int value = (array[i] ? 1 : 0); nElemWritten = fprintf(file->handle, "%d", value), */
|
||||
/* true) */
|
||||
|
||||
/* Note that we do a trick */
|
||||
READ_WRITE_METHODS(unsigned char, Byte,
|
||||
nread = fread(data, 1, n, dfself->handle); break,
|
||||
nwrite = fwrite(data, 1, n, dfself->handle); break)
|
||||
|
||||
READ_WRITE_METHODS(char, Char,
|
||||
nread = fread(data, 1, n, dfself->handle); break,
|
||||
nwrite = fwrite(data, 1, n, dfself->handle); break)
|
||||
|
||||
READ_WRITE_METHODS(short, Short,
|
||||
int ret = fscanf(dfself->handle, "%hd", &data[i]); if(ret <= 0) break; else nread++,
|
||||
int ret = fprintf(dfself->handle, "%hd", data[i]); if(ret <= 0) break; else nwrite++)
|
||||
|
||||
READ_WRITE_METHODS(int, Int,
|
||||
int ret = fscanf(dfself->handle, "%d\n\r", &data[i]); if(ret <= 0) break; else nread++,
|
||||
int ret = fprintf(dfself->handle, "%d", data[i]); if(ret <= 0) break; else nwrite++)
|
||||
|
||||
/*READ_WRITE_METHODS(long, Long,
|
||||
int ret = fscanf(dfself->handle, "%ld", &data[i]); if(ret <= 0) break; else nread++,
|
||||
int ret = fprintf(dfself->handle, "%ld", data[i]); if(ret <= 0) break; else nwrite++)*/
|
||||
|
||||
READ_WRITE_METHODS(float, Float,
|
||||
int ret = fscanf(dfself->handle, "%g", &data[i]); if(ret <= 0) break; else nread++,
|
||||
int ret = fprintf(dfself->handle, "%.9g", data[i]); if(ret <= 0) break; else nwrite++)
|
||||
|
||||
READ_WRITE_METHODS(double, Double,
|
||||
int ret = fscanf(dfself->handle, "%lg", &data[i]); if(ret <= 0) break; else nread++,
|
||||
int ret = fprintf(dfself->handle, "%.17g", data[i]); if(ret <= 0) break; else nwrite++)
|
||||
|
||||
|
||||
/* For Long we need to rewrite everything, because of the special management of longSize */
|
||||
static long THDiskFile_readLong(THFile *self, int64 *data, long n)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
long nread = 0L;
|
||||
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
THArgCheck(dfself->file.isReadable, 1, "attempt to read in a write-only file");
|
||||
|
||||
if(dfself->file.isBinary)
|
||||
{
|
||||
if(dfself->longSize == 0 || dfself->longSize == sizeof(int64))
|
||||
{
|
||||
nread = fread__(data, sizeof(int64), n, dfself->handle);
|
||||
if(!dfself->isNativeEncoding && (sizeof(int64) > 1) && (nread > 0))
|
||||
THDiskFile_reverseMemory(data, data, sizeof(int64), nread);
|
||||
} else if(dfself->longSize == 4)
|
||||
{
|
||||
nread = fread__(data, 4, n, dfself->handle);
|
||||
if(!dfself->isNativeEncoding && (nread > 0))
|
||||
THDiskFile_reverseMemory(data, data, 4, nread);
|
||||
long i;
|
||||
for(i = nread; i > 0; i--)
|
||||
data[i-1] = ((int *)data)[i-1];
|
||||
}
|
||||
else /* if(dfself->longSize == 8) */
|
||||
{
|
||||
int big_endian = !THDiskFile_isLittleEndianCPU();
|
||||
int32_t *buffer = (int32_t*)THAlloc(8*n);
|
||||
nread = fread__(buffer, 8, n, dfself->handle);
|
||||
long i;
|
||||
for(i = nread; i > 0; i--)
|
||||
data[i-1] = buffer[2*(i-1) + big_endian];
|
||||
THFree(buffer);
|
||||
if(!dfself->isNativeEncoding && (nread > 0))
|
||||
THDiskFile_reverseMemory(data, data, 4, nread);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
long i;
|
||||
for(i = 0; i < n; i++)
|
||||
{
|
||||
long d;
|
||||
int ret = fscanf(dfself->handle, "%ld", &d); if(ret <= 0) break; else nread++;
|
||||
data[i] = d;
|
||||
}
|
||||
if(dfself->file.isAutoSpacing && (n > 0))
|
||||
{
|
||||
int c = fgetc(dfself->handle);
|
||||
if( (c != '\n') && (c != EOF) )
|
||||
ungetc(c, dfself->handle);
|
||||
}
|
||||
}
|
||||
|
||||
if(nread != n)
|
||||
{
|
||||
dfself->file.hasError = 1; /* shouldn't we put hasError to 0 all the time ? */
|
||||
if(!dfself->file.isQuiet)
|
||||
THError("read error: read %d blocks instead of %d", nread, n);
|
||||
}
|
||||
|
||||
return nread;
|
||||
}
|
||||
|
||||
static long THDiskFile_writeLong(THFile *self, int64 *data, long n)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
long nwrite = 0L;
|
||||
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
THArgCheck(dfself->file.isWritable, 1, "attempt to write in a read-only file");
|
||||
|
||||
if(dfself->file.isBinary)
|
||||
{
|
||||
if(dfself->longSize == 0 || dfself->longSize == sizeof(long))
|
||||
{
|
||||
if(dfself->isNativeEncoding)
|
||||
{
|
||||
nwrite = fwrite(data, sizeof(long), n, dfself->handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *buffer = (char*)THAlloc(sizeof(long)*n);
|
||||
THDiskFile_reverseMemory(buffer, data, sizeof(long), n);
|
||||
nwrite = fwrite(buffer, sizeof(long), n, dfself->handle);
|
||||
THFree(buffer);
|
||||
}
|
||||
} else if(dfself->longSize == 4)
|
||||
{
|
||||
int32_t *buffer = (int32_t *)THAlloc(4*n);
|
||||
long i;
|
||||
for(i = 0; i < n; i++)
|
||||
buffer[i] = data[i];
|
||||
if(!dfself->isNativeEncoding)
|
||||
THDiskFile_reverseMemory(buffer, buffer, 4, n);
|
||||
nwrite = fwrite(buffer, 4, n, dfself->handle);
|
||||
THFree(buffer);
|
||||
}
|
||||
else /* if(dfself->longSize == 8) */
|
||||
{
|
||||
int big_endian = !THDiskFile_isLittleEndianCPU();
|
||||
int32_t *buffer = (int32_t*)THAlloc(8*n);
|
||||
long i;
|
||||
for(i = 0; i < n; i++)
|
||||
{
|
||||
buffer[2*i + !big_endian] = 0;
|
||||
buffer[2*i + big_endian] = data[i];
|
||||
}
|
||||
if(!dfself->isNativeEncoding)
|
||||
THDiskFile_reverseMemory(buffer, buffer, 8, n);
|
||||
nwrite = fwrite(buffer, 8, n, dfself->handle);
|
||||
THFree(buffer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
long i;
|
||||
for(i = 0; i < n; i++)
|
||||
{
|
||||
long res = 0;
|
||||
int ret = fprintf(dfself->handle, "%ld", res); data[i] = res; if(ret <= 0) break; else nwrite++;
|
||||
if( dfself->file.isAutoSpacing && (i < n-1) )
|
||||
fprintf(dfself->handle, " ");
|
||||
}
|
||||
if(dfself->file.isAutoSpacing && (n > 0))
|
||||
fprintf(dfself->handle, "\n");
|
||||
}
|
||||
|
||||
if(nwrite != n)
|
||||
{
|
||||
dfself->file.hasError = 1;
|
||||
if(!dfself->file.isQuiet)
|
||||
THError("write error: wrote %d blocks instead of %d", nwrite, n);
|
||||
}
|
||||
|
||||
return nwrite;
|
||||
}
|
||||
|
||||
static long THDiskFile_readString(THFile *self, const char *format, char **str_)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
THArgCheck(dfself->file.isReadable, 1, "attempt to read in a write-only file");
|
||||
THArgCheck((strlen(format) >= 2 ? (format[0] == '*') && (format[1] == 'a' || format[1] == 'l') : 0), 2, "format must be '*a' or '*l'");
|
||||
|
||||
/* note: the string won't survive long, as it is copied into lua */
|
||||
/* so 1024 is not that big... */
|
||||
#define TBRS_BSZ 1024L
|
||||
|
||||
if(format[1] == 'a')
|
||||
{
|
||||
char *p = (char*)THAlloc(TBRS_BSZ);
|
||||
long total = TBRS_BSZ;
|
||||
long pos = 0L;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if(total-pos == 0) /* we need more space! */
|
||||
{
|
||||
total += TBRS_BSZ;
|
||||
p = (char*)THRealloc(p, total);
|
||||
}
|
||||
pos += fread(p+pos, 1, total-pos, dfself->handle);
|
||||
if (pos < total) /* eof? */
|
||||
{
|
||||
if(pos == 0L)
|
||||
{
|
||||
THFree(p);
|
||||
dfself->file.hasError = 1;
|
||||
if(!dfself->file.isQuiet)
|
||||
THError("read error: read 0 blocks instead of 1");
|
||||
|
||||
*str_ = NULL;
|
||||
return 0;
|
||||
}
|
||||
*str_ = p;
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char *p = (char*)THAlloc(TBRS_BSZ);
|
||||
long total = TBRS_BSZ;
|
||||
long pos = 0L;
|
||||
long size;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
if(total-pos <= 1) /* we can only write '\0' in there! */
|
||||
{
|
||||
total += TBRS_BSZ;
|
||||
p = (char*)THRealloc(p, total);
|
||||
}
|
||||
if (fgets(p+pos, total-pos, dfself->handle) == NULL) /* eof? */
|
||||
{
|
||||
if(pos == 0L)
|
||||
{
|
||||
THFree(p);
|
||||
dfself->file.hasError = 1;
|
||||
if(!dfself->file.isQuiet)
|
||||
THError("read error: read 0 blocks instead of 1");
|
||||
|
||||
*str_ = NULL;
|
||||
return 0;
|
||||
}
|
||||
*str_ = p;
|
||||
return pos;
|
||||
}
|
||||
size = strlen(p+pos);
|
||||
if (size == 0L || (p+pos)[size-1] != '\n')
|
||||
{
|
||||
pos += size;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos += size-1L; /* do not include `eol' */
|
||||
*str_ = p;
|
||||
return pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*str_ = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static long THDiskFile_writeString(THFile *self, const char *str, long size)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
long nwrite;
|
||||
|
||||
THArgCheck(dfself->handle != NULL, 1, "attempt to use a closed file");
|
||||
THArgCheck(dfself->file.isWritable, 1, "attempt to write in a read-only file");
|
||||
|
||||
nwrite = fwrite(str, 1, size, dfself->handle);
|
||||
if(nwrite != size)
|
||||
{
|
||||
dfself->file.hasError = 1;
|
||||
if(!dfself->file.isQuiet)
|
||||
THError("write error: wrote %ld blocks instead of %ld", nwrite, size);
|
||||
}
|
||||
|
||||
return nwrite;
|
||||
}
|
||||
|
||||
THFile *THDiskFile_new(const char *name, const char *mode, int isQuiet)
|
||||
{
|
||||
static struct THFileVTable vtable = {
|
||||
THDiskFile_isOpened,
|
||||
|
||||
THDiskFile_readByte,
|
||||
THDiskFile_readChar,
|
||||
THDiskFile_readShort,
|
||||
THDiskFile_readInt,
|
||||
THDiskFile_readLong,
|
||||
THDiskFile_readFloat,
|
||||
THDiskFile_readDouble,
|
||||
THDiskFile_readString,
|
||||
|
||||
THDiskFile_writeByte,
|
||||
THDiskFile_writeChar,
|
||||
THDiskFile_writeShort,
|
||||
THDiskFile_writeInt,
|
||||
THDiskFile_writeLong,
|
||||
THDiskFile_writeFloat,
|
||||
THDiskFile_writeDouble,
|
||||
THDiskFile_writeString,
|
||||
|
||||
THDiskFile_synchronize,
|
||||
THDiskFile_seek,
|
||||
THDiskFile_seekEnd,
|
||||
THDiskFile_position,
|
||||
THDiskFile_close,
|
||||
THDiskFile_free
|
||||
};
|
||||
|
||||
int isReadable;
|
||||
int isWritable;
|
||||
FILE *handle;
|
||||
THDiskFile *self;
|
||||
|
||||
THArgCheck(THDiskFile_mode(mode, &isReadable, &isWritable), 2, "file mode should be 'r','w' or 'rw'");
|
||||
|
||||
if( isReadable && isWritable )
|
||||
{
|
||||
handle = fopen(name, "r+b");
|
||||
if(!handle)
|
||||
{
|
||||
handle = fopen(name, "wb");
|
||||
if(handle)
|
||||
{
|
||||
fclose(handle);
|
||||
handle = fopen(name, "r+b");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
handle = fopen(name, (isReadable ? "rb" : "wb"));
|
||||
|
||||
if(!handle)
|
||||
{
|
||||
if(isQuiet)
|
||||
return 0;
|
||||
else
|
||||
THError("cannot open <%s> in mode %c%c", name, (isReadable ? 'r' : ' '), (isWritable ? 'w' : ' '));
|
||||
}
|
||||
|
||||
self = (THDiskFile*)THAlloc(sizeof(THDiskFile));
|
||||
|
||||
self->handle = handle;
|
||||
self->name = (char*)THAlloc(strlen(name)+1);
|
||||
strcpy(self->name, name);
|
||||
self->isNativeEncoding = 1;
|
||||
self->longSize = 0;
|
||||
|
||||
self->file.vtable = &vtable;
|
||||
self->file.isQuiet = isQuiet;
|
||||
self->file.isReadable = isReadable;
|
||||
self->file.isWritable = isWritable;
|
||||
self->file.isBinary = 0;
|
||||
self->file.isAutoSpacing = 1;
|
||||
self->file.hasError = 0;
|
||||
|
||||
return (THFile*)self;
|
||||
}
|
||||
|
||||
/* PipeFile */
|
||||
|
||||
static int THPipeFile_mode(const char *mode, int *isReadable, int *isWritable)
|
||||
{
|
||||
*isReadable = 0;
|
||||
*isWritable = 0;
|
||||
if(strlen(mode) == 1)
|
||||
{
|
||||
if(*mode == 'r')
|
||||
{
|
||||
*isReadable = 1;
|
||||
return 1;
|
||||
}
|
||||
else if(*mode == 'w')
|
||||
{
|
||||
*isWritable = 1;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void THPipeFile_free(THFile *self)
|
||||
{
|
||||
THDiskFile *dfself = (THDiskFile*)(self);
|
||||
if(dfself->handle)
|
||||
pclose(dfself->handle);
|
||||
THFree(dfself->name);
|
||||
THFree(dfself);
|
||||
}
|
||||
|
||||
THFile *THPipeFile_new(const char *name, const char *mode, int isQuiet)
|
||||
{
|
||||
static struct THFileVTable vtable = {
|
||||
THDiskFile_isOpened,
|
||||
|
||||
THDiskFile_readByte,
|
||||
THDiskFile_readChar,
|
||||
THDiskFile_readShort,
|
||||
THDiskFile_readInt,
|
||||
THDiskFile_readLong,
|
||||
THDiskFile_readFloat,
|
||||
THDiskFile_readDouble,
|
||||
THDiskFile_readString,
|
||||
|
||||
THDiskFile_writeByte,
|
||||
THDiskFile_writeChar,
|
||||
THDiskFile_writeShort,
|
||||
THDiskFile_writeInt,
|
||||
THDiskFile_writeLong,
|
||||
THDiskFile_writeFloat,
|
||||
THDiskFile_writeDouble,
|
||||
THDiskFile_writeString,
|
||||
|
||||
THDiskFile_synchronize,
|
||||
THDiskFile_seek,
|
||||
THDiskFile_seekEnd,
|
||||
THDiskFile_position,
|
||||
THDiskFile_close,
|
||||
THPipeFile_free
|
||||
};
|
||||
|
||||
int isReadable;
|
||||
int isWritable;
|
||||
FILE *handle;
|
||||
THDiskFile *self;
|
||||
|
||||
THArgCheck(THPipeFile_mode(mode, &isReadable, &isWritable), 2, "file mode should be 'r','w'");
|
||||
|
||||
#ifdef _WIN32
|
||||
handle = popen(name, (isReadable ? "rb" : "wb"));
|
||||
#else
|
||||
handle = popen(name, (isReadable ? "r" : "w"));
|
||||
#endif
|
||||
|
||||
if(!handle)
|
||||
{
|
||||
if(isQuiet)
|
||||
return 0;
|
||||
else
|
||||
THError("cannot open <%s> in mode %c%c", name, (isReadable ? 'r' : ' '), (isWritable ? 'w' : ' '));
|
||||
}
|
||||
|
||||
self = (THDiskFile*)THAlloc(sizeof(THDiskFile));
|
||||
|
||||
self->handle = handle;
|
||||
self->name = (char*)THAlloc(strlen(name)+1);
|
||||
strcpy(self->name, name);
|
||||
self->isNativeEncoding = 1;
|
||||
self->longSize = 0;
|
||||
|
||||
self->file.vtable = &vtable;
|
||||
self->file.isQuiet = isQuiet;
|
||||
self->file.isReadable = isReadable;
|
||||
self->file.isWritable = isWritable;
|
||||
self->file.isBinary = 0;
|
||||
self->file.isAutoSpacing = 1;
|
||||
self->file.hasError = 0;
|
||||
|
||||
return (THFile*)self;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
#ifndef TH_DISK_FILE_INC
|
||||
#define TH_DISK_FILE_INC
|
||||
|
||||
#include "THFile.h"
|
||||
|
||||
TH_API THFile *THDiskFile_new(const char *name, const char *mode, int isQuiet);
|
||||
TH_API THFile *THPipeFile_new(const char *name, const char *mode, int isQuiet);
|
||||
|
||||
TH_API const char *THDiskFile_name(THFile *self);
|
||||
|
||||
TH_API int THDiskFile_isLittleEndianCPU(void);
|
||||
TH_API int THDiskFile_isBigEndianCPU(void);
|
||||
TH_API void THDiskFile_nativeEndianEncoding(THFile *self);
|
||||
TH_API void THDiskFile_littleEndianEncoding(THFile *self);
|
||||
TH_API void THDiskFile_bigEndianEncoding(THFile *self);
|
||||
TH_API void THDiskFile_longSize(THFile *self, int size);
|
||||
TH_API void THDiskFile_noBuffer(THFile *self);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,163 @@
|
||||
#if defined(ENABLE_TORCH_IMPORTER) && ENABLE_TORCH_IMPORTER
|
||||
#include "THFile.h"
|
||||
#include "THFilePrivate.h"
|
||||
#include <opencv2/core.hpp>
|
||||
extern "C"
|
||||
{
|
||||
|
||||
#define IMPLEMENT_THFILE_RW(TYPEC, TYPE) \
|
||||
long THFile_read##TYPEC##Raw(THFile *self, TYPE *data, long n) \
|
||||
{ \
|
||||
return (*self->vtable->read##TYPEC)(self, data, n); \
|
||||
} \
|
||||
\
|
||||
long THFile_write##TYPEC##Raw(THFile *self, TYPE *data, long n) \
|
||||
{ \
|
||||
return (*self->vtable->write##TYPEC)(self, data, n); \
|
||||
}
|
||||
|
||||
IMPLEMENT_THFILE_RW(Byte, unsigned char)
|
||||
IMPLEMENT_THFILE_RW(Char, char)
|
||||
IMPLEMENT_THFILE_RW(Short, short)
|
||||
IMPLEMENT_THFILE_RW(Int, int)
|
||||
IMPLEMENT_THFILE_RW(Long, int64)
|
||||
IMPLEMENT_THFILE_RW(Float, float)
|
||||
IMPLEMENT_THFILE_RW(Double, double)
|
||||
|
||||
long THFile_readStringRaw(THFile *self, const char *format, char **str_)
|
||||
{
|
||||
return self->vtable->readString(self, format, str_);
|
||||
}
|
||||
|
||||
long THFile_writeStringRaw(THFile *self, const char *str, long size)
|
||||
{
|
||||
return self->vtable->writeString(self, str, size);
|
||||
}
|
||||
|
||||
void THFile_synchronize(THFile *self)
|
||||
{
|
||||
self->vtable->synchronize(self);
|
||||
}
|
||||
|
||||
void THFile_seek(THFile *self, long position)
|
||||
{
|
||||
self->vtable->seek(self, position);
|
||||
}
|
||||
|
||||
void THFile_seekEnd(THFile *self)
|
||||
{
|
||||
self->vtable->seekEnd(self);
|
||||
}
|
||||
|
||||
long THFile_position(THFile *self)
|
||||
{
|
||||
return self->vtable->position(self);
|
||||
}
|
||||
|
||||
void THFile_close(THFile *self)
|
||||
{
|
||||
self->vtable->close(self);
|
||||
}
|
||||
|
||||
void THFile_free(THFile *self)
|
||||
{
|
||||
self->vtable->free(self);
|
||||
}
|
||||
|
||||
int THFile_isOpened(THFile *self)
|
||||
{
|
||||
return self->vtable->isOpened(self);
|
||||
}
|
||||
|
||||
#define IMPLEMENT_THFILE_FLAGS(FLAG) \
|
||||
int THFile_##FLAG(THFile *self) \
|
||||
{ \
|
||||
return self->FLAG; \
|
||||
}
|
||||
|
||||
IMPLEMENT_THFILE_FLAGS(isQuiet)
|
||||
IMPLEMENT_THFILE_FLAGS(isReadable)
|
||||
IMPLEMENT_THFILE_FLAGS(isWritable)
|
||||
IMPLEMENT_THFILE_FLAGS(isBinary)
|
||||
IMPLEMENT_THFILE_FLAGS(isAutoSpacing)
|
||||
IMPLEMENT_THFILE_FLAGS(hasError)
|
||||
|
||||
void THFile_binary(THFile *self)
|
||||
{
|
||||
self->isBinary = 1;
|
||||
}
|
||||
|
||||
void THFile_ascii(THFile *self)
|
||||
{
|
||||
self->isBinary = 0;
|
||||
}
|
||||
|
||||
void THFile_autoSpacing(THFile *self)
|
||||
{
|
||||
self->isAutoSpacing = 1;
|
||||
}
|
||||
|
||||
void THFile_noAutoSpacing(THFile *self)
|
||||
{
|
||||
self->isAutoSpacing = 0;
|
||||
}
|
||||
|
||||
void THFile_quiet(THFile *self)
|
||||
{
|
||||
self->isQuiet = 1;
|
||||
}
|
||||
|
||||
void THFile_pedantic(THFile *self)
|
||||
{
|
||||
self->isQuiet = 0;
|
||||
}
|
||||
|
||||
void THFile_clearError(THFile *self)
|
||||
{
|
||||
self->hasError = 0;
|
||||
}
|
||||
|
||||
#define IMPLEMENT_THFILE_SCALAR(TYPEC, TYPE) \
|
||||
TYPE THFile_read##TYPEC##Scalar(THFile *self) \
|
||||
{ \
|
||||
TYPE scalar; \
|
||||
THFile_read##TYPEC##Raw(self, &scalar, 1); \
|
||||
return scalar; \
|
||||
} \
|
||||
\
|
||||
void THFile_write##TYPEC##Scalar(THFile *self, TYPE scalar) \
|
||||
{ \
|
||||
THFile_write##TYPEC##Raw(self, &scalar, 1); \
|
||||
}
|
||||
|
||||
IMPLEMENT_THFILE_SCALAR(Byte, unsigned char)
|
||||
IMPLEMENT_THFILE_SCALAR(Char, char)
|
||||
IMPLEMENT_THFILE_SCALAR(Short, short)
|
||||
IMPLEMENT_THFILE_SCALAR(Int, int)
|
||||
IMPLEMENT_THFILE_SCALAR(Long, int64)
|
||||
IMPLEMENT_THFILE_SCALAR(Float, float)
|
||||
IMPLEMENT_THFILE_SCALAR(Double, double)
|
||||
|
||||
/*
|
||||
#define IMPLEMENT_THFILE_STORAGE(TYPEC, TYPE) \
|
||||
long THFile_read##TYPEC(THFile *self, TH##TYPEC##Storage *storage) \
|
||||
{ \
|
||||
return THFile_read##TYPEC##Raw(self, storage->data, storage->size); \
|
||||
} \
|
||||
\
|
||||
long THFile_write##TYPEC(THFile *self, TH##TYPEC##Storage *storage) \
|
||||
{ \
|
||||
return THFile_write##TYPEC##Raw(self, storage->data, storage->size); \
|
||||
}
|
||||
|
||||
IMPLEMENT_THFILE_STORAGE(Byte, unsigned char)
|
||||
IMPLEMENT_THFILE_STORAGE(Char, char)
|
||||
IMPLEMENT_THFILE_STORAGE(Short, short)
|
||||
IMPLEMENT_THFILE_STORAGE(Int, int)
|
||||
IMPLEMENT_THFILE_STORAGE(Long, long)
|
||||
IMPLEMENT_THFILE_STORAGE(Float, float)
|
||||
IMPLEMENT_THFILE_STORAGE(Double, double)
|
||||
*/
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef TH_FILE_INC
|
||||
#define TH_FILE_INC
|
||||
|
||||
//#include "THStorage.h"
|
||||
#if defined(ENABLE_TORCH_IMPORTER) && ENABLE_TORCH_IMPORTER
|
||||
#include "THGeneral.h"
|
||||
#include "opencv2/core/hal/interface.h"
|
||||
|
||||
typedef struct THFile__ THFile;
|
||||
|
||||
TH_API int THFile_isOpened(THFile *self);
|
||||
TH_API int THFile_isQuiet(THFile *self);
|
||||
TH_API int THFile_isReadable(THFile *self);
|
||||
TH_API int THFile_isWritable(THFile *self);
|
||||
TH_API int THFile_isBinary(THFile *self);
|
||||
TH_API int THFile_isAutoSpacing(THFile *self);
|
||||
TH_API int THFile_hasError(THFile *self);
|
||||
|
||||
TH_API void THFile_binary(THFile *self);
|
||||
TH_API void THFile_ascii(THFile *self);
|
||||
TH_API void THFile_autoSpacing(THFile *self);
|
||||
TH_API void THFile_noAutoSpacing(THFile *self);
|
||||
TH_API void THFile_quiet(THFile *self);
|
||||
TH_API void THFile_pedantic(THFile *self);
|
||||
TH_API void THFile_clearError(THFile *self);
|
||||
|
||||
/* scalar */
|
||||
TH_API unsigned char THFile_readByteScalar(THFile *self);
|
||||
TH_API char THFile_readCharScalar(THFile *self);
|
||||
TH_API short THFile_readShortScalar(THFile *self);
|
||||
TH_API int THFile_readIntScalar(THFile *self);
|
||||
TH_API int64 THFile_readLongScalar(THFile *self);
|
||||
TH_API float THFile_readFloatScalar(THFile *self);
|
||||
TH_API double THFile_readDoubleScalar(THFile *self);
|
||||
|
||||
TH_API void THFile_writeByteScalar(THFile *self, unsigned char scalar);
|
||||
TH_API void THFile_writeCharScalar(THFile *self, char scalar);
|
||||
TH_API void THFile_writeShortScalar(THFile *self, short scalar);
|
||||
TH_API void THFile_writeIntScalar(THFile *self, int scalar);
|
||||
TH_API void THFile_writeLongScalar(THFile *self, int64 scalar);
|
||||
TH_API void THFile_writeFloatScalar(THFile *self, float scalar);
|
||||
TH_API void THFile_writeDoubleScalar(THFile *self, double scalar);
|
||||
|
||||
/* storage */
|
||||
/*
|
||||
TH_API long THFile_readByte(THFile *self, THByteStorage *storage);
|
||||
TH_API long THFile_readChar(THFile *self, THCharStorage *storage);
|
||||
TH_API long THFile_readShort(THFile *self, THShortStorage *storage);
|
||||
TH_API long THFile_readInt(THFile *self, THIntStorage *storage);
|
||||
TH_API long THFile_readLong(THFile *self, THLongStorage *storage);
|
||||
TH_API long THFile_readFloat(THFile *self, THFloatStorage *storage);
|
||||
TH_API long THFile_readDouble(THFile *self, THDoubleStorage *storage);
|
||||
|
||||
TH_API long THFile_writeByte(THFile *self, THByteStorage *storage);
|
||||
TH_API long THFile_writeChar(THFile *self, THCharStorage *storage);
|
||||
TH_API long THFile_writeShort(THFile *self, THShortStorage *storage);
|
||||
TH_API long THFile_writeInt(THFile *self, THIntStorage *storage);
|
||||
TH_API long THFile_writeLong(THFile *self, THLongStorage *storage);
|
||||
TH_API long THFile_writeFloat(THFile *self, THFloatStorage *storage);
|
||||
TH_API long THFile_writeDouble(THFile *self, THDoubleStorage *storage);
|
||||
*/
|
||||
|
||||
/* raw */
|
||||
TH_API long THFile_readByteRaw(THFile *self, unsigned char *data, long n);
|
||||
TH_API long THFile_readCharRaw(THFile *self, char *data, long n);
|
||||
TH_API long THFile_readShortRaw(THFile *self, short *data, long n);
|
||||
TH_API long THFile_readIntRaw(THFile *self, int *data, long n);
|
||||
TH_API long THFile_readLongRaw(THFile *self, int64 *data, long n);
|
||||
TH_API long THFile_readFloatRaw(THFile *self, float *data, long n);
|
||||
TH_API long THFile_readDoubleRaw(THFile *self, double *data, long n);
|
||||
TH_API long THFile_readStringRaw(THFile *self, const char *format, char **str_); /* you must deallocate str_ */
|
||||
|
||||
TH_API long THFile_writeByteRaw(THFile *self, unsigned char *data, long n);
|
||||
TH_API long THFile_writeCharRaw(THFile *self, char *data, long n);
|
||||
TH_API long THFile_writeShortRaw(THFile *self, short *data, long n);
|
||||
TH_API long THFile_writeIntRaw(THFile *self, int *data, long n);
|
||||
TH_API long THFile_writeLongRaw(THFile *self, int64 *data, long n);
|
||||
TH_API long THFile_writeFloatRaw(THFile *self, float *data, long n);
|
||||
TH_API long THFile_writeDoubleRaw(THFile *self, double *data, long n);
|
||||
TH_API long THFile_writeStringRaw(THFile *self, const char *str, long size);
|
||||
|
||||
TH_API void THFile_synchronize(THFile *self);
|
||||
TH_API void THFile_seek(THFile *self, long position);
|
||||
TH_API void THFile_seekEnd(THFile *self);
|
||||
TH_API long THFile_position(THFile *self);
|
||||
TH_API void THFile_close(THFile *self);
|
||||
TH_API void THFile_free(THFile *self);
|
||||
|
||||
#endif //defined(ENABLE_TORCH_IMPORTER) && ENABLE_TORCH_IMPORTER
|
||||
#endif //TH_FILE_INC
|
||||
@@ -0,0 +1,43 @@
|
||||
struct THFile__
|
||||
{
|
||||
struct THFileVTable *vtable;
|
||||
|
||||
int isQuiet;
|
||||
int isReadable;
|
||||
int isWritable;
|
||||
int isBinary;
|
||||
int isAutoSpacing;
|
||||
int hasError;
|
||||
};
|
||||
|
||||
/* virtual table definition */
|
||||
|
||||
struct THFileVTable
|
||||
{
|
||||
int (*isOpened)(THFile *self);
|
||||
|
||||
long (*readByte)(THFile *self, unsigned char *data, long n);
|
||||
long (*readChar)(THFile *self, char *data, long n);
|
||||
long (*readShort)(THFile *self, short *data, long n);
|
||||
long (*readInt)(THFile *self, int *data, long n);
|
||||
long (*readLong)(THFile *self, int64 *data, long n);
|
||||
long (*readFloat)(THFile *self, float *data, long n);
|
||||
long (*readDouble)(THFile *self, double *data, long n);
|
||||
long (*readString)(THFile *self, const char *format, char **str_);
|
||||
|
||||
long (*writeByte)(THFile *self, unsigned char *data, long n);
|
||||
long (*writeChar)(THFile *self, char *data, long n);
|
||||
long (*writeShort)(THFile *self, short *data, long n);
|
||||
long (*writeInt)(THFile *self, int *data, long n);
|
||||
long (*writeLong)(THFile *self, int64 *data, long n);
|
||||
long (*writeFloat)(THFile *self, float *data, long n);
|
||||
long (*writeDouble)(THFile *self, double *data, long n);
|
||||
long (*writeString)(THFile *self, const char *str, long size);
|
||||
|
||||
void (*synchronize)(THFile *self);
|
||||
void (*seek)(THFile *self, long position);
|
||||
void (*seekEnd)(THFile *self);
|
||||
long (*position)(THFile *self);
|
||||
void (*close)(THFile *self);
|
||||
void (*free)(THFile *self);
|
||||
};
|
||||
@@ -0,0 +1,256 @@
|
||||
#if defined(ENABLE_TORCH_IMPORTER) && ENABLE_TORCH_IMPORTER
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#if defined(TH_DISABLE_HEAP_TRACKING)
|
||||
#elif (defined(__unix) || defined(_WIN32))
|
||||
#include <malloc.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <malloc/malloc.h>
|
||||
#endif
|
||||
|
||||
#include "THGeneral.h"
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
#ifndef TH_HAVE_THREAD
|
||||
#define __thread
|
||||
#endif
|
||||
|
||||
/* Torch Error Handling */
|
||||
static void defaultTorchErrorHandlerFunction(const char *msg, void*)
|
||||
{
|
||||
CV_Error(cv::Error::StsError, cv::String("Torch Error: ") + msg);
|
||||
}
|
||||
|
||||
static __thread void (*torchErrorHandlerFunction)(const char *msg, void *data) = defaultTorchErrorHandlerFunction;
|
||||
static __thread void *torchErrorHandlerData;
|
||||
|
||||
void _THError(const char *file, const int line, const char *fmt, ...)
|
||||
{
|
||||
char msg[2048];
|
||||
va_list args;
|
||||
|
||||
/* vasprintf not standard */
|
||||
/* vsnprintf: how to handle if does not exists? */
|
||||
va_start(args, fmt);
|
||||
int n = vsnprintf(msg, 2048, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if(n < 2048) {
|
||||
snprintf(msg + n, 2048 - n, " at %s:%d", file, line);
|
||||
}
|
||||
|
||||
(*torchErrorHandlerFunction)(msg, torchErrorHandlerData);
|
||||
}
|
||||
|
||||
void _THAssertionFailed(const char *file, const int line, const char *exp, const char *fmt, ...) {
|
||||
char msg[1024];
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vsnprintf(msg, 1024, fmt, args);
|
||||
va_end(args);
|
||||
_THError(file, line, "Assertion `%s' failed. %s", exp, msg);
|
||||
}
|
||||
|
||||
void THSetErrorHandler( void (*torchErrorHandlerFunction_)(const char *msg, void *data), void *data )
|
||||
{
|
||||
if(torchErrorHandlerFunction_)
|
||||
torchErrorHandlerFunction = torchErrorHandlerFunction_;
|
||||
else
|
||||
torchErrorHandlerFunction = defaultTorchErrorHandlerFunction;
|
||||
torchErrorHandlerData = data;
|
||||
}
|
||||
|
||||
/* Torch Arg Checking Handling */
|
||||
static void defaultTorchArgErrorHandlerFunction(int argNumber, const char *msg, void*)
|
||||
{
|
||||
if(msg)
|
||||
CV_Error(cv::Error::StsError, cv::format("Torch invalid argument %d: %s", argNumber, msg));
|
||||
else
|
||||
CV_Error(cv::Error::StsError, cv::format("Invalid argument %d", argNumber));
|
||||
}
|
||||
|
||||
static __thread void (*torchArgErrorHandlerFunction)(int argNumber, const char *msg, void *data) = defaultTorchArgErrorHandlerFunction;
|
||||
static __thread void *torchArgErrorHandlerData;
|
||||
|
||||
void _THArgCheck(const char *file, int line, int condition, int argNumber, const char *fmt, ...)
|
||||
{
|
||||
if(!condition) {
|
||||
char msg[2048];
|
||||
va_list args;
|
||||
|
||||
/* vasprintf not standard */
|
||||
/* vsnprintf: how to handle if does not exists? */
|
||||
va_start(args, fmt);
|
||||
int n = vsnprintf(msg, 2048, fmt, args);
|
||||
va_end(args);
|
||||
|
||||
if(n < 2048) {
|
||||
snprintf(msg + n, 2048 - n, " at %s:%d", file, line);
|
||||
}
|
||||
|
||||
(*torchArgErrorHandlerFunction)(argNumber, msg, torchArgErrorHandlerData);
|
||||
}
|
||||
}
|
||||
|
||||
void THSetArgErrorHandler( void (*torchArgErrorHandlerFunction_)(int argNumber, const char *msg, void *data), void *data )
|
||||
{
|
||||
if(torchArgErrorHandlerFunction_)
|
||||
torchArgErrorHandlerFunction = torchArgErrorHandlerFunction_;
|
||||
else
|
||||
torchArgErrorHandlerFunction = defaultTorchArgErrorHandlerFunction;
|
||||
torchArgErrorHandlerData = data;
|
||||
}
|
||||
|
||||
static __thread void (*torchGCFunction)(void *data) = NULL;
|
||||
static __thread void *torchGCData;
|
||||
static __thread long torchHeapSize = 0;
|
||||
static __thread long torchHeapSizeSoftMax = 300000000; // 300MB, adjusted upward dynamically
|
||||
|
||||
/* Optional hook for integrating with a garbage-collected frontend.
|
||||
*
|
||||
* If torch is running with a garbage-collected frontend (e.g. Lua),
|
||||
* the GC isn't aware of TH-allocated memory so may not know when it
|
||||
* needs to run. These hooks trigger the GC to run in two cases:
|
||||
*
|
||||
* (1) When a memory allocation (malloc, realloc, ...) fails
|
||||
* (2) When the total TH-allocated memory hits a dynamically-adjusted
|
||||
* soft maximum.
|
||||
*/
|
||||
void THSetGCHandler( void (*torchGCFunction_)(void *data), void *data )
|
||||
{
|
||||
torchGCFunction = torchGCFunction_;
|
||||
torchGCData = data;
|
||||
}
|
||||
|
||||
static long getAllocSize(void *ptr) {
|
||||
#if defined(TH_DISABLE_HEAP_TRACKING)
|
||||
return 0;
|
||||
#elif defined(__unix)
|
||||
return malloc_usable_size(ptr);
|
||||
#elif defined(__APPLE__)
|
||||
return malloc_size(ptr);
|
||||
#elif defined(_WIN32)
|
||||
return _msize(ptr);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* (1) if the torch-allocated heap size exceeds the soft max, run GC
|
||||
* (2) if post-GC heap size exceeds 80% of the soft max, increase the
|
||||
* soft max by 40%
|
||||
*/
|
||||
static void maybeTriggerGC() {
|
||||
if(torchGCFunction && torchHeapSize > torchHeapSizeSoftMax) {
|
||||
torchGCFunction(torchGCData);
|
||||
if(torchHeapSize > torchHeapSizeSoftMax * 0.8) {
|
||||
torchHeapSizeSoftMax = torchHeapSizeSoftMax * 1.4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hooks into the TH heap tracking
|
||||
void THHeapUpdate(long size) {
|
||||
torchHeapSize += size;
|
||||
if (size > 0)
|
||||
maybeTriggerGC();
|
||||
}
|
||||
|
||||
static void* THAllocInternal(long size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
if (size > 5120)
|
||||
{
|
||||
#if (defined(__unix) || defined(__APPLE__)) && (!defined(DISABLE_POSIX_MEMALIGN))
|
||||
if (posix_memalign(&ptr, 64, size) != 0)
|
||||
ptr = NULL;
|
||||
/*
|
||||
#elif defined(_WIN32)
|
||||
ptr = _aligned_malloc(size, 64);
|
||||
*/
|
||||
#else
|
||||
ptr = malloc(size);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = malloc(size);
|
||||
}
|
||||
|
||||
THHeapUpdate(getAllocSize(ptr));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* THAlloc(long size)
|
||||
{
|
||||
void *ptr;
|
||||
|
||||
if(size < 0)
|
||||
THError("$ Torch: invalid memory size -- maybe an overflow?");
|
||||
|
||||
if(size == 0)
|
||||
return NULL;
|
||||
|
||||
ptr = THAllocInternal(size);
|
||||
|
||||
if(!ptr && torchGCFunction) {
|
||||
torchGCFunction(torchGCData);
|
||||
ptr = THAllocInternal(size);
|
||||
}
|
||||
|
||||
if(!ptr)
|
||||
THError("$ Torch: not enough memory: you tried to allocate %dGB. Buy new RAM!", size/1073741824);
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* THRealloc(void *ptr, long size)
|
||||
{
|
||||
if(!ptr)
|
||||
return(THAlloc(size));
|
||||
|
||||
if(size == 0)
|
||||
{
|
||||
THFree(ptr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if(size < 0)
|
||||
THError("$ Torch: invalid memory size -- maybe an overflow?");
|
||||
|
||||
THHeapUpdate(-getAllocSize(ptr));
|
||||
void *newptr = realloc(ptr, size);
|
||||
|
||||
if(!newptr && torchGCFunction) {
|
||||
torchGCFunction(torchGCData);
|
||||
newptr = realloc(ptr, size);
|
||||
}
|
||||
THHeapUpdate(getAllocSize(newptr ? newptr : ptr));
|
||||
|
||||
if(!newptr)
|
||||
THError("$ Torch: not enough memory: you tried to reallocate %dGB. Buy new RAM!", size/1073741824);
|
||||
|
||||
return newptr;
|
||||
}
|
||||
|
||||
void THFree(void *ptr)
|
||||
{
|
||||
THHeapUpdate(-getAllocSize(ptr));
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
double THLog1p(const double x)
|
||||
{
|
||||
#if (defined(_MSC_VER) || defined(__MINGW32__))
|
||||
volatile double y = 1 + x;
|
||||
return log(y) - ((y-1)-x)/y ; /* cancels errors with IEEE arithmetic */
|
||||
#else
|
||||
return log1p(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef TH_GENERAL_INC
|
||||
#define TH_GENERAL_INC
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <math.h>
|
||||
#include <limits.h>
|
||||
#include <float.h>
|
||||
#include <time.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
# define TH_EXTERNC extern "C"
|
||||
#else
|
||||
# define TH_EXTERNC extern
|
||||
#endif
|
||||
|
||||
#define TH_API TH_EXTERNC
|
||||
|
||||
#define THInf DBL_MAX
|
||||
|
||||
//#define TH_INLINE @TH_INLINE@
|
||||
|
||||
#ifndef __cplusplus
|
||||
//#define inline @TH_INLINE@
|
||||
#endif
|
||||
|
||||
#ifndef M_PI
|
||||
# define M_PI 3.14159265358979323846
|
||||
#endif
|
||||
|
||||
TH_API double THLog1p(const double x);
|
||||
TH_API void _THError(const char *file, const int line, const char *fmt, ...);
|
||||
TH_API void _THAssertionFailed(const char *file, const int line, const char *exp, const char *fmt, ...);
|
||||
TH_API void THSetErrorHandler( void (*torchErrorHandlerFunction)(const char *msg, void *data), void *data );
|
||||
TH_API void _THArgCheck(const char *file, int line, int condition, int argNumber, const char *fmt, ...);
|
||||
TH_API void THSetArgErrorHandler( void (*torchArgErrorHandlerFunction)(int argNumber, const char *msg, void *data), void *data );
|
||||
TH_API void* THAlloc(long size);
|
||||
TH_API void* THRealloc(void *ptr, long size);
|
||||
TH_API void THFree(void *ptr);
|
||||
TH_API void THSetGCHandler( void (*torchGCHandlerFunction)(void *data), void *data );
|
||||
// this hook should only be called by custom allocator functions
|
||||
TH_API void THHeapUpdate(long size);
|
||||
|
||||
#define THError(...) _THError(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#define THArgCheck(...) _THArgCheck(__FILE__, __LINE__, __VA_ARGS__)
|
||||
#define THAssert(exp) \
|
||||
do { \
|
||||
if (!(exp)) { \
|
||||
_THAssertionFailed(__FILE__, __LINE__, #exp, ""); \
|
||||
} \
|
||||
} while(0)
|
||||
#define THAssertMsg(exp, ...) \
|
||||
do { \
|
||||
if (!(exp)) { \
|
||||
_THAssertionFailed(__FILE__, __LINE__, #exp, __VA_ARGS__); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define TH_CONCAT_STRING_2(x,y) TH_CONCAT_STRING_2_EXPAND(x,y)
|
||||
#define TH_CONCAT_STRING_2_EXPAND(x,y) #x #y
|
||||
|
||||
#define TH_CONCAT_STRING_3(x,y,z) TH_CONCAT_STRING_3_EXPAND(x,y,z)
|
||||
#define TH_CONCAT_STRING_3_EXPAND(x,y,z) #x #y #z
|
||||
|
||||
#define TH_CONCAT_STRING_4(x,y,z,w) TH_CONCAT_STRING_4_EXPAND(x,y,z,w)
|
||||
#define TH_CONCAT_STRING_4_EXPAND(x,y,z,w) #x #y #z #w
|
||||
|
||||
#define TH_CONCAT_2(x,y) TH_CONCAT_2_EXPAND(x,y)
|
||||
#define TH_CONCAT_2_EXPAND(x,y) x ## y
|
||||
|
||||
#define TH_CONCAT_3(x,y,z) TH_CONCAT_3_EXPAND(x,y,z)
|
||||
#define TH_CONCAT_3_EXPAND(x,y,z) x ## y ## z
|
||||
|
||||
#define TH_CONCAT_4_EXPAND(x,y,z,w) x ## y ## z ## w
|
||||
#define TH_CONCAT_4(x,y,z,w) TH_CONCAT_4_EXPAND(x,y,z,w)
|
||||
|
||||
#define THMin(X, Y) ((X) < (Y) ? (X) : (Y))
|
||||
#define THMax(X, Y) ((X) > (Y) ? (X) : (Y))
|
||||
|
||||
#if (defined(_MSC_VER) || defined(__MINGW32__))
|
||||
# define log1p(x) THLog1p(x)
|
||||
#define snprintf _snprintf
|
||||
#define popen _popen
|
||||
#define pclose _pclose
|
||||
#endif
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user