Search code examples
ctensorflowdeeplab

Tensorflow: TF_SessionRun returns TF_INVALID_ARGUMENT


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

Solution

  • 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.