1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

Import SSDs from TensorFlow by training config (#12188)

* Remove TensorFlow and protobuf dependencies from object detection scripts

* Create text graphs for TensorFlow object detection networks from sample
This commit is contained in:
Dmitry Kurtaev
2018-09-03 17:08:40 +03:00
committed by Vadim Pisarevsky
parent e3af72bb68
commit c7cf8fb35c
10 changed files with 858 additions and 450 deletions
+8
View File
@@ -885,6 +885,14 @@ CV__DNN_EXPERIMENTAL_NS_BEGIN
CV_EXPORTS_W void shrinkCaffeModel(const String& src, const String& dst,
const std::vector<String>& layersTypes = std::vector<String>());
/** @brief Create a text representation for a binary network stored in protocol buffer format.
* @param[in] model A path to binary network.
* @param[in] output A path to output text file to be created.
*
* @note To reduce output file size, trained weights are not included.
*/
CV_EXPORTS_W void writeTextGraph(const String& model, const String& output);
/** @brief Performs non maximum suppression given boxes and corresponding scores.
* @param bboxes a set of bounding boxes to apply NMS.
@@ -782,6 +782,108 @@ void releaseTensor(tensorflow::TensorProto* tensor)
}
}
static void permute(google::protobuf::RepeatedPtrField<tensorflow::NodeDef>* data,
const std::vector<int>& indices)
{
const int num = data->size();
CV_Assert(num == indices.size());
std::vector<int> elemIdToPos(num);
std::vector<int> posToElemId(num);
for (int i = 0; i < num; ++i)
{
elemIdToPos[i] = i;
posToElemId[i] = i;
}
for (int i = 0; i < num; ++i)
{
int elemId = indices[i];
int pos = elemIdToPos[elemId];
if (pos != i)
{
data->SwapElements(i, pos);
const int swappedElemId = posToElemId[i];
elemIdToPos[elemId] = i;
elemIdToPos[swappedElemId] = pos;
posToElemId[i] = elemId;
posToElemId[pos] = swappedElemId;
}
}
}
// Is based on tensorflow::graph_transforms::SortByExecutionOrder
void sortByExecutionOrder(tensorflow::GraphDef& net)
{
// Maps node's name to index at net.node() list.
std::map<std::string, int> nodesMap;
std::map<std::string, int>::iterator nodesMapIt;
for (int i = 0; i < net.node_size(); ++i)
{
const tensorflow::NodeDef& node = net.node(i);
nodesMap.insert(std::make_pair(node.name(), i));
}
// Indices of nodes which use specific node as input.
std::vector<std::vector<int> > edges(nodesMap.size());
std::vector<int> numRefsToAdd(nodesMap.size(), 0);
std::vector<int> nodesToAdd;
for (int i = 0; i < net.node_size(); ++i)
{
const tensorflow::NodeDef& node = net.node(i);
for (int j = 0; j < node.input_size(); ++j)
{
std::string inpName = node.input(j);
inpName = inpName.substr(0, inpName.rfind(':'));
inpName = inpName.substr(inpName.find('^') + 1);
nodesMapIt = nodesMap.find(inpName);
CV_Assert(nodesMapIt != nodesMap.end());
edges[nodesMapIt->second].push_back(i);
}
if (node.input_size() == 0)
nodesToAdd.push_back(i);
else
{
if (node.op() == "Merge" || node.op() == "RefMerge")
{
int numControlEdges = 0;
for (int j = 0; j < node.input_size(); ++j)
numControlEdges += node.input(j)[0] == '^';
numRefsToAdd[i] = numControlEdges + 1;
}
else
numRefsToAdd[i] = node.input_size();
}
}
std::vector<int> permIds;
permIds.reserve(net.node_size());
while (!nodesToAdd.empty())
{
int nodeToAdd = nodesToAdd.back();
nodesToAdd.pop_back();
permIds.push_back(nodeToAdd);
// std::cout << net.node(nodeToAdd).name() << '\n';
for (int i = 0; i < edges[nodeToAdd].size(); ++i)
{
int consumerId = edges[nodeToAdd][i];
if (numRefsToAdd[consumerId] > 0)
{
if (numRefsToAdd[consumerId] == 1)
nodesToAdd.push_back(consumerId);
else
CV_Assert(numRefsToAdd[consumerId] >= 0);
numRefsToAdd[consumerId] -= 1;
}
}
}
CV_Assert(permIds.size() == net.node_size());
permute(net.mutable_node(), permIds);
}
CV__DNN_EXPERIMENTAL_NS_END
}} // namespace dnn, namespace cv
@@ -25,6 +25,8 @@ Mat getTensorContent(const tensorflow::TensorProto &tensor);
void releaseTensor(tensorflow::TensorProto* tensor);
void sortByExecutionOrder(tensorflow::GraphDef& net);
CV__DNN_EXPERIMENTAL_NS_END
}} // namespace dnn, namespace cv
@@ -1950,5 +1950,34 @@ Net readNetFromTensorflow(const std::vector<uchar>& bufferModel, const std::vect
bufferConfigPtr, bufferConfig.size());
}
void writeTextGraph(const String& _model, const String& output)
{
String model = _model;
const std::string modelExt = model.substr(model.rfind('.') + 1);
if (modelExt != "pb")
CV_Error(Error::StsNotImplemented, "Only TensorFlow models support export to text file");
tensorflow::GraphDef net;
ReadTFNetParamsFromBinaryFileOrDie(model.c_str(), &net);
sortByExecutionOrder(net);
RepeatedPtrField<tensorflow::NodeDef>::iterator it;
for (it = net.mutable_node()->begin(); it != net.mutable_node()->end(); ++it)
{
if (it->op() == "Const")
{
it->mutable_attr()->at("value").mutable_tensor()->clear_tensor_content();
}
}
std::string content;
google::protobuf::TextFormat::PrintToString(net, &content);
std::ofstream ofs(output.c_str());
ofs << content;
ofs.close();
}
CV__DNN_EXPERIMENTAL_NS_END
}} // namespace
+25 -1
View File
@@ -315,6 +315,29 @@ TEST_P(Test_TensorFlow_nets, Inception_v2_SSD)
normAssertDetections(ref, out, "", 0.5, scoreDiff, iouDiff);
}
TEST_P(Test_TensorFlow_nets, MobileNet_v1_SSD)
{
checkBackend();
std::string model = findDataFile("dnn/ssd_mobilenet_v1_coco_2017_11_17.pb", false);
std::string proto = findDataFile("dnn/ssd_mobilenet_v1_coco_2017_11_17.pbtxt", false);
Net net = readNetFromTensorflow(model, proto);
Mat img = imread(findDataFile("dnn/dog416.png", false));
Mat blob = blobFromImage(img, 1.0f, Size(300, 300), Scalar(), true, false);
net.setPreferableBackend(backend);
net.setPreferableTarget(target);
net.setInput(blob);
Mat out = net.forward();
Mat ref = blobFromNPY(findDataFile("dnn/tensorflow/ssd_mobilenet_v1_coco_2017_11_17.detection_out.npy"));
float scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 7e-3 : 1e-5;
float iouDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.0098 : 1e-3;
normAssertDetections(ref, out, "", 0.3, scoreDiff, iouDiff);
}
TEST_P(Test_TensorFlow_nets, Faster_RCNN)
{
static std::string names[] = {"faster_rcnn_inception_v2_coco_2018_01_28",
@@ -360,7 +383,8 @@ TEST_P(Test_TensorFlow_nets, MobileNet_v1_SSD_PPN)
net.setInput(blob);
Mat out = net.forward();
double scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.008 : default_l1;
double scoreDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.011 : default_l1;
double iouDiff = (target == DNN_TARGET_OPENCL_FP16 || target == DNN_TARGET_MYRIAD) ? 0.021 : default_lInf;
normAssertDetections(ref, out, "", 0.4, scoreDiff, iouDiff);
}