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:
committed by
Vadim Pisarevsky
parent
e3af72bb68
commit
c7cf8fb35c
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user