I am running a session from a frozen graph of Deeplabv3 using the Tensorflow C API. When I get to the part of running the session with TF_SessionRun
, the return value is 3, indicating TF_INVALID_ARGUMENT
. I suspect it may have to do something with the TF_Operation*
input (the 8th argument aka "Target Operations" argument) which I left NULL
, but I cannot find any documentation as to what this argument represents. Below is my problematic invocation of TF_SessionRun:
from tiny_deeplab_api.cpp:
// Allocate the input tensor
TF_Tensor* const input = TF_NewTensor(TF_UINT8, img->dims, 3, img->data_ptr, img->bytes, &free_tensor, NULL);
TF_Operation* oper_in = TF_GraphOperationByName(graph, "ImageTensor");
const TF_Output oper_in_ = {oper_in, 0};
// Allocate the output tensor
TF_Tensor* output = TF_NewTensor(TF_UINT8, seg->dims, 2, seg->data_ptr, seg->bytes, &free_tensor, NULL);
TF_Operation* oper_out = TF_GraphOperationByName(graph, "SemanticPredictions");
const TF_Output oper_out_ = {oper_out, 0};
// Run the session on the input tensor
TF_SessionRun(session, NULL, &oper_in_, &input, 1, &oper_out_, &output, 1, NULL, 0, NULL, status);
return TF_GetCode(status); // https://github.com/tensorflow/tensorflow/blob/master/tensorflow/c/tf_status.h#L42
where img
and seg
are image_t
and segmap_t
types which contain pointers to the data and to a dimension array that the TF_NewTensor()
method can use to make input and output tensors to then pass to TF_SessionRun()
. (from tiny_deeplab_api.hpp):
typedef struct segmap {
const int64_t* dims;
size_t bytes;
uint8_t* data_ptr;
} segmap_t;
typedef struct image {
const int64_t* dims;
size_t bytes;
uint8_t* data_ptr;
} image_t;
Below is the source code in case the problem is not obvious...
test.cpp:
#include <opencv2/opencv.hpp>
#include "tiny_deeplab_api.hpp"
#include <iostream>
#include <algorithm>
int main() {
using namespace std;
using namespace cv;
// Initialize Deeplab object
Deeplab dl = Deeplab();
cout << "Successfully constructed Deeplab object" << endl;
// Read & resize input image
Mat image = imread("/Users/Daniel/Desktop/cat.jpg");
int orig_height = image.size().height;
int orig_width = image.size().width;
double resize_ratio = (double) 513 / max(orig_height, orig_width);
Size new_size((int)(resize_ratio*orig_width), (int)(resize_ratio*orig_height));
Mat resized_image;
resize(image, resized_image, new_size);
cout << "Image resized (h, w): (" << orig_height << "," << orig_width << ") --> (" << new_size.height << ", " << new_size.width << ")" << endl;
imshow("Image", resized_image);
waitKey(0);
// Allocate input image object
const int64_t dims_in[3] = {new_size.width, new_size.height, 3};
image_t* img_in = (image_t*)malloc(sizeof(image_t));
img_in->dims = &dims_in[0];
img_in->data_ptr = resized_image.data;
img_in->bytes = new_size.width*new_size.height*3*sizeof(uint8_t);
// Allocate output segmentation map object
const int64_t dims_out[2] = {new_size.width, new_size.height};
segmap_t* seg_out = (segmap_t*)malloc(sizeof(segmap_t));
seg_out->dims = &dims_out[0];
seg_out->data_ptr = (uint8_t*)malloc(new_size.width*new_size.height);
seg_out->bytes = new_size.width*new_size.height*sizeof(uint8_t);
// Run Deeplab
cout << "Running segmentation" << endl;
int status = dl.run_segmentation(img_in, seg_out);
if(status != 0) {
cout << "ERROR RUNNING SEGMENTATION: " << status << endl;
return 1;
}
cout << "Successfully ran segmentation" << endl;
// Interpret results
return 0;
}
tiny_deeplab_api.hpp:
#ifndef TINY_DEEPLAB_API_HPP_
#define TINY_DEEPLAB_API_HPP_
#include <tensorflow/c/c_api.h>
TF_Buffer* read_file(const char* file);
void free_buffer(void* data, size_t length);
void free_tensor(void* data, size_t length, void* args);
typedef struct segmap {
const int64_t* dims;
size_t bytes;
uint8_t* data_ptr;
} segmap_t;
typedef struct image {
const int64_t* dims;
size_t bytes;
uint8_t* data_ptr;
} image_t;
class Deeplab {
private:
TF_Session* session;
TF_Graph* graph;
TF_Output output_oper;
TF_Output input_oper;
TF_Status* status;
public:
Deeplab(); // Constructor
~Deeplab();
int run_segmentation(image_t*, segmap_t*);
};
#endif // TINY_DEEPLAB_API_HPP_
tiny_deeplab_api.cpp:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <tensorflow/c/c_api.h>
#include "tiny_deeplab_api.hpp"
Deeplab::Deeplab() {
using namespace std;
cout << "Hello from TensorFlow C library version" << TF_Version() << endl;
// Import Deeplab graph (as a frozen graph, it has the weights hard-coded in as constants, so no need to restore the checkpoint)
TF_Buffer* graph_def = read_file("../Models/Deeplab_model_unpacked/deeplabv3_mnv2_cityscapes_train/frozen_inference_graph.pb");
graph = TF_NewGraph();
status = TF_NewStatus();
TF_ImportGraphDefOptions* opts = TF_NewImportGraphDefOptions();
TF_GraphImportGraphDef(graph, graph_def, opts, status);
TF_DeleteImportGraphDefOptions(opts);
if (TF_GetCode(status) != TF_OK) {
fprintf(stderr, "ERROR: Unable to import graph %s", TF_Message(status));
return;
}
cout << "Successfully loaded Deeplab graph" << endl;
TF_DeleteBuffer(graph_def);
// Initialize Session
TF_SessionOptions* sess_opts = TF_NewSessionOptions();
session = TF_NewSession(graph, sess_opts, status);
}
Deeplab::~Deeplab() {
using namespace std;
TF_CloseSession(session, status);
TF_DeleteSession(session, status);
TF_DeleteStatus(status);
TF_DeleteGraph(graph);
cout << "Destroyed Deeplab object" << endl;
}
int Deeplab::run_segmentation(image_t* img, segmap_t* seg) {
//TODO: Delete old TF_Tensor, TF_Operation, and TF_Output
// Allocate the input tensor
TF_Tensor* const input = TF_NewTensor(TF_UINT8, img->dims, 3, img->data_ptr, img->bytes, &free_tensor, NULL);
TF_Operation* oper_in = TF_GraphOperationByName(graph, "ImageTensor");
const TF_Output oper_in_ = {oper_in, 0};
// Allocate the output tensor
TF_Tensor* output = TF_NewTensor(TF_UINT8, seg->dims, 2, seg->data_ptr, seg->bytes, &free_tensor, NULL);
TF_Operation* oper_out = TF_GraphOperationByName(graph, "SemanticPredictions");
const TF_Output oper_out_ = {oper_out, 0};
// Run the session on the input tensor
TF_SessionRun(session, NULL, &oper_in_, &input, 1, &oper_out_, &output, 1, NULL, 0, NULL, status);
return TF_GetCode(status); // https://github.com/tensorflow/tensorflow/blob/master/tensorflow/c/tf_status.h#L42
}
TF_Buffer* read_file(const char* file) {
FILE *f = fopen(file, "rb");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
fseek(f, 0, SEEK_SET); //same as rewind(f);
void* data = malloc(fsize);
fread(data, fsize, 1, f);
fclose(f);
TF_Buffer* buf = TF_NewBuffer();
buf->data = data;
buf->length = fsize;
buf->data_deallocator = free_buffer;
return buf;
}
void free_buffer(void* data, size_t length) {
free(data);
}
void free_tensor(void* data, size_t length, void* args) {
free(data);
}
And the output of running ./test
:
Hello from TensorFlow C library version1.14.0
Successfully loaded Deeplab graph
2019-08-25 13:40:06.947965: I tensorflow/core/platform/cpu_feature_guard.cc:142] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2 FMA
Successfully constructed Deeplab object
Image resized (h, w): (1680,2987) --> (288, 513)
Running segmentation
ERROR RUNNING SEGMENTATION: 3
Destroyed Deeplab object
The answer is that, for some reason (why?) the dimensions of Deeplab's input and output tensors are NOT {width, height, 3} and {width, height}, but {1, width, height, 3} and {1, width, height}. After making dimension arrays of this form, TF_SessionRun ran without errors.