serizba / cppflow

Run TensorFlow models in C++ without installation and without Bazel
https://serizba.github.io/cppflow/
MIT License
774 stars 177 forks source link

Insert Tensors in Tensor | RGB Image by Channels #236

Closed nische closed 1 year ago

nische commented 1 year ago

Hi, I use cppflow to Predict Images with a Classifier. This works fine with my OpenCV BGR Images. In my new Usecase i get three Pointers (to every Channel of the RGB). In the first Steps i push the data together in one vector and create the tensor. This work but is very slow and not usefull for my Project.

Is it possible to create a tensor from each Channel and stick them together in one tensor?

Here is my function to predict with the single rgb pointer:

int Classifier::Predigt(uint8_t* img_data, int width, int height, int channels)
    {
        std::vector<uint8_t> img;
        img.assign(img_data, img_data + (width*height*channels));
        auto img_tensor = cppflow::tensor(img, {height, width, channels});
        img_tensor = cppflow::expand_dims(img_tensor, 0);
        if (width != _model->width || height != _model->height)
            img_tensor= cppflow::resize_bicubic(img_tensor, {224, 224});
        img_tensor = cppflow::cast(img_tensor, TF_UINT8, TF_FLOAT);

        auto output = (*_model)({{_model->inputOpName, img_tensor}}, {"StatefulPartitionedCall:0"});
        return cppflow::arg_max(output[0], 1).get_data<int64_t>()[0];
    } 

This is the slow vector solution:

int Classifier::Predigt(uint8_t* red,  uint8_t* green, uint8_t* blue, int width, int height)
    {
        int n = (height*width);
        std::vector<uint8_t> img_data;
        img_data.reserve(3*n);
        for (int i = 0; i < n; i++)
        {
            img_data.push_back(red[i]);
            img_data.push_back(green[i]);
            img_data.push_back(blue[i]);
        }
        return Predigt(img_data.data(), width, height, 3);
    }

Maybe something like this is possible or did everybody know some magic tensor tricks to reorganize the tensor after it created with the vector from rrrr...rrrrgggggg....gggggbbbbbb.....bbbbbb type?

    int Classifier::Predigt(uint8_t* red,  uint8_t* green, uint8_t* blue, int width, int height)
    {
        std::vector<uint8_t> r;
        r.assign(red, red + (width*height));
        auto r_tensor = cppflow::tensor(r, {height, width});

        std::vector<uint8_t> g;
        g.assign(green, green + (width*height));
        auto g_tensor = cppflow::tensor(g, {height, width});

        std::vector<uint8_t> b;
        b.assign(blue, blue + (width*height));
        auto b_tensor = cppflow::tensor(b, {height, width});

        //auto img_tensor = cppflow::tensor({r,g,b}, {height, width, 3});
        auto img_tensor = cppflow::tensor({r,g,b}); <---- runtime error: Could not deduce type! type_name: class std::vector<unsigned char,class std::allocator<unsigned char> >

        return -1;
    }

BR, Nische

nische commented 1 year ago

Hi, After I studied my old university documents i found a perfomant solution:

First I create the tensor in the shape of rgb per layer. After that i "reorganize" the Tensor by Transpose it with the vector {1,2,0}.

int Classifier::Predigt(uint8_t* red,  uint8_t* green, uint8_t* blue, int width, int height)
    {
        int n = (height*width);
        std::vector<uint8_t> data;
        data.reserve(3*n);
        data.insert(data.end(), red, red + n);
        data.insert(data.end(), green, green + n);
        data.insert(data.end(), blue, blue + n);
        auto img_tensor = cppflow::tensor(data, {3, height, width});
        img_tensor = cppflow::transpose(img_tensor, cppflow::tensor{1,2,0});
        .....
 }