Subtensor of a Tensorflow tensor (C++) - c++

I have a tensorflow::Tensor batch in C++ with shape [2, 720, 1280, 3] (#images x height x width x #channels).
I want to get another tensor with only the first image, thus I would have a tensor of shape [1, 720, 1280, 3]. In order words, I want:
tensorflow::Tensor first = batch[0]
What's the most efficient way to achieve it?
I know how to do this in python, but the C++ api and documentation are not as good as python's.

After spending some time trying to implement through copy, I realised that this operation is supported in the API as Slice:
tensorflow::Tensor first = batch.Slice(0, 1);
Note that, as documented, the returned tensor shares the internal buffer with the sliced one, and the alignment of both tensors may be different, if that is relevant to you.
EDIT:
Since I had already done it, here is my attempt at reproducing the same functionality, copy-based. I think it should work (it is pretty similar to what I use in other context).
#include <cstdlib>
#include <cassert>
#include <tensorflow/core/framework/tensor.h>
#include <tensorflow/core/framework/tensor_shape.h>
tensorflow::Tensor get_element(const tensorflow::Tensor data, unsigned int index, bool keepDim)
{
using namespace std;
using namespace tensorflow;
typedef typename tensorflow::DataTypeToEnum<T> DataType;
auto dtype = DataType::v();
assert(dtype == data.dtype());
auto dtype = data.dtype();
auto dataShape = data.shape();
TensorShape elementShape;
if (keepDim)
{
elementShape.addDim(1);
}
for (int iDim = 1; iDim < dataShape.dims(); iDim++) {
elementShape.AddDim(dataShape.dim_size(iDim));
}
Tensor element(dtype, elementShape);
auto elementBytes = elementShape.num_elements() * DataTypeSize(dtype);
memcpy(element.flat<void>().data(),
batch.flat<void>().data() + elementBytes * index,
elementBytes);
return element;
}
int main()
{
Tensor batch = ...;
Tensor first = get_element(batch, 0);
return 0;
}
The code can also be changed if you just want to extract the data to, for example, a vector or something else.

This works fine
#include "tensorflow/core/framework/tensor_slice.h"
Tensor t2 = t1.Slice(0,1);

Related

Iterating the creation of objects in C++

I want to be able to create N skyscrapers. Using an inputdata string, I would like to give them coordinate values of their X and Y positions. My main function I used "i" to demonstrate that I am trying to create as many skyscrapers as I can using the input data. Essentially, I would like to create N/3 skyscrapers and assign the input to coordinates for each.
#include <iostream>
#include <vector>
#include <string>
#include <math.h>
using namespace std;
vector<int> inputData = {1, 4, 10, 3, 5, 7, 9, 10, 4, 11, 3, 2, 14, 5, 5};
int N = inputData.size();
class Buildings{
public:
int yCoordinateLow;
int yCoordinateHigh;
int xCoordinateLeft;
int xCoordinateRight;
};
int main(){
for(int i=0; i<N; i=i+3){
Buildings skyscraper;
skyscraper.xCoordianteLeft = inputData.at(i);
skyscraper.yCoordianteLow = 0;
skyscraper.yCoordinateHigh = inputData.at(i+1);
skyscraper.xCoordinateRight = inputData.at(i+2);
}
return 0;
}
Jeff Atwood once said: use the best tools money can buy. And those aren't even expensive: Visual Studio community edition is free. Such a proper IDE will tell you that the skyscraper is unused except for the assignments.
Since you probably want to do something with those skyscrapers later, you should store them somewhere, e.g. in another vector.
int main() {
vector<Buildings> skyscrapers;
for (int i = 0; i < N; i = i + 3) {
Buildings skyscraper{};
skyscraper.xCoordinateLeft = inputData.at(i);
skyscraper.yCoordinateLow = 0;
skyscraper.yCoordinateHigh = inputData.at(i + 1);
skyscraper.xCoordinateRight = inputData.at(i + 2);
skyscrapers.push_back(skyscraper);
}
return 0;
}
Other than that, I'd say the loop works fine as long as there are N*3 coordinates in the original vector.
If you e.g. implement a game, you would probably not hard code the skyscraper coordinates in a vector but rather read that data from a file, potentially per level.
Instead of doing all the error-prone coding, maybe you want to initialize the skyscrapers immediately
vector<Buildings> skyscrapers = {{1,0,4,10}, {3,0,5,7}, {9,0,10,4}, {11,0,3,4}, {14,0,5,5}};

Tensorflow tflite c++ api inference for matrix data array

I am creating a class that will be used to run inference on an embedded device (not raspberry pi) in c++ using tensorflow's tflite c++ api. Tensorflow doesn't seem to have decent documentation on how to run inference for n number of samples of image data. My data shape in python is (n, 5, 40, 1) [n samples, 5 height, 40 width, 1 channel]. What I cannot figure out is how to input the data and receive the inference per sample in the output. I have two classes so I should receive n 2-d array ouputs. Does anyone know if you can pass in any data type such as an Eigen? I am testing with an input of shape (1, 5, 2, 1) to simplify my test.
#include "classifier.h"
#include <iostream>
using namespace tflite;
Classifier::Classifier(std::string modelPath) {
tflite::StderrReporter error_reporter;
model = tflite::FlatBufferModel::BuildFromFile(modelPath.c_str(), &error_reporter);
tflite::ops::builtin::BuiltinOpResolver resolver;
tflite::InterpreterBuilder(*model, resolver)(&interpreter); // private class variable interpreter
std::vector<int> sizes = {1, 5, 2, 1};
interpreter->ResizeInputTensor(0, sizes);
interpreter->AllocateTensors();
}
std::vector<std::vector<float> Classifier::getDataSamples() {
std::vector<std::vector<float> test = {{0.02, 0.02}, {0.02, 0.02}, {0.02, 0.02},{0.02, 0.02},{0.02, 0.02},};
return test;
}
float Classifier::predict() {
std::vector<float> signatures = getDataSamples();
for (int i = 0; i < signatures.size(); ++i) {
interpreter->typed_input_tensor<float>(0)[i];
}
// float* input = interpreter->typed_input_tensor<float>(0);
// *input = 1.0;
interpreter->Invoke();
float* output = interpreter->typed_output_tensor<float>(0);
return *output;
}
From the Tensorflow documentation we can find below details,
It should be noted that:
Tensors are represented by integers, in order to avoid string comparisons (and any fixed dependency on string libraries).
An interpreter must not be accessed from concurrent threads.
Memory allocation for input and output tensors must be triggered by calling AllocateTensors() right after resizing tensors.
You can find more about the Load and run a model in C++ here.

Adding a custom sparse op (Sparse Determinant)

I am working on trying to get some sparse matrix operations working in Tensorflow. The first one I am tackling is a sparse determinant, via a sparse Cholesky decomposition. Eigen has a sparse Cholesky, so my thought is to wrap that.
I have been making some progress, but am now a little bit stuck. I know that SparseTensors in Tensorflow are made up of three parts: indices, values, and shape. Copying similar ops, I went for the following REGISTER_OP declaration:
REGISTER_OP("SparseLogDet")
.Input("a_indices: int64")
.Input("a_values: float32")
.Input("a_shape: int64")
.Output("determinant: float32")
.SetShapeFn([](::tensorflow::shape_inference::InferenceContext* c) {
shape_inference::ShapeHandle h;
c->set_output(0, h);
return Status::OK();
});
This compiles fine, but when I run it using some example code:
import tensorflow as tf
log_det_op = tf.load_op_library('./sparse_log_det_op.so')
with tf.Session(''):
t = tf.SparseTensor(indices=[[0, 0], [1, 2]], values=[1, 2],
dense_shape=[3, 4])
print(log_det_op.sparse_log_det(t).eval().shape)
print(log_det_op.sparse_log_det(t).eval())
It complains, saying:
TypeError: sparse_log_det() missing 2 required positional arguments: 'a_values' and 'a_shape'
This makes sense to me, since it's expecting the other arguments. However, I would really just like to pass the sparse tensor, not break it up into components! Does anyone know how this is handled for other sparse operations?
Thanks!
If you want to pass in the sparse tensor and then determine indices, values and shape from this, this should be possible. Just modify your OP to take a single Tensor input, and produce a single float output. Then extract the desired information form the Eigen::Tensor by looping through its elements as seen below:
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/framework/shape_inference.h"
#include "tensorflow/core/framework/op_kernel.h"
#include <Eigen/Dense>
using namespace tensorflow;
REGISTER_OP("SparseDeterminant")
.Input("sparse_tensor: float")
.Output("sparse_determinant: float");
class SparseDeterminantOp : public OpKernel {
public:
explicit SparseDeterminantOp(OpKernelConstruction *context) : OpKernel(context) {}
void Compute(OpKernelContext *context) override {
// get the input tesnorflow tensor
const Tensor& sparse_tensor = context->input(0);
// get shape of input
const TensorShape& sparse_shape = sparse_tensor.shape();
// get Eigen Tensor for input tensor
auto eigen_sparse = sparse_tensor.matrix<float>();
//extract the data you want from the sparse tensor input
auto a_shape = sparse_tensor.shape();
// loop over all elements of the input tensor and add to values and indices
for (int i=0; i<a_shape.dim_size(0); ++i){
for (int j=0; j<a_shape.dim_size(1); ++j){
if(eigen_sparse(i,j) != 0){
/// ***Here add non zero elements to list/tensor of values and their indicies***
std::cout<<eigen_sparse(i,j)<<" at"<<" "<<i<<" "<<j<<" "<<"not zero."<<std::endl;
}
}
}
// create output tensor
Tensor *output_tensor = NULL;
TensorShape output_shape;
OP_REQUIRES_OK(context, context->allocate_output(0, output_shape, &output_tensor));
auto output = output_tensor->scalar<float>();
output(0) = 1.; //**asign return value***;
}
};
REGISTER_KERNEL_BUILDER(Name("SparseDeterminant").Device(DEVICE_CPU), SparseDeterminantOp);
sadly, when you pass t into your op it becomes a Tensorflow::Tensor and loses the values and indices methods associated with tf.sparsetensor, so you can't get them easily.
Once compiled this code can be run with:
//run.py
import tensorflow as tf
import numpy as np
my_module = tf.load_op_library('./out.so')
# create sparse matrix
a = np.zeros((10,10))
for i in range(len(a)):
a[i,i] = i
print(a)
a_t = tf.convert_to_tensor(a, dtype= float)
with tf.Session() as sess:
sess.run(my_module.sparse_determinant(a_t))

Trying to make a live data grapher with CImg library (C++)

I'm new to CImg. Not sure if there's already a live data plotter in the library but I thought I'd go ahead and make one myself. If what I'm looking for already exists in the library please point me to the function. otherwise, here is my super inefficient code that I'm hoping you can help me with~
#include <iostream>
#include "CImg.h"
#include <ctime>
#include <cmath>
using namespace cimg_library;
int main()
{
CImg<unsigned char> plot(400, 320, 1, 3, 0);
CImgDisplay graph(plot, "f(x)");
clock();
const unsigned char red[] = {255, 0, 0};
float* G = new float[plot.width()]; //define an array holding the values that are to be displayed on the graph
while (1){
G[0] = ((plot.height()/4) * sin(clock() / 1000.0)) + plot.height()/2; // new f(t) value
for (int i = 1; i <= plot.width() - 1; i++){
G[plot.width() - i] = G[plot.width() - i - 1]; //basically shift all the array values to current address+1
plot.draw_point(plot.width() - 3*i, G[i-1], red, 1).display(graph);
}
plot.fill(0);
}
return 0;
}
problems
the grapher traverses right to left soo slowly.. and I'm not sure how to make a smooth curve hence I went with points.. how do you make a smooth curve?
There is already something for you in the library, method CImg<T>::draw_graph(), as (brielfy) explained here :
http://cimg.eu/reference/structcimg__library_1_1CImg.html#a2e629aadedc4518001f00333f25bfec8
There are few examples provided with the library that use this method, see files examples/tutorial.cpp and examples/plotter1d.cpp.

How to assign values already stored in fixed memory to a cimg object

I am using the Cimg lib to do the image processing work. I have the pointer of an array returned by GPU, and I want to the Cimg object to take the value of the array directly. Now, I am using the for loops to do the work, but it is not very efficient.
The sample codes I am using now is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include "CImg.h"
using namespace std;
using namespace cimg_library;
int main(){
char pause;
int width=3; int height=3;//Dimension of the Cimg matrix
int size = width * height * sizeof(int); // number of bytes in total in arrays
int *ptr = (int*) malloc(size);
//Assign the array some example value;
for (int m=0;m<width*height;m++){
*(ptr+m)=m;
}
CImg<int> img(width,height,1,1);
//This is how I assign the value of the array to the Cimg object
for (int i=0;i<width;i++)
for (int j=0;j<height;j++)
{
img.atXY(i,j)=*(ptr+j*width+i);
}
img.save_bmp("img.bmp");
cout<<"Pause:";
cin>>pause;
}
}
I tired this code, but it does not work:
img.begin()=ptr;
Anyone can help me to eliminate the for loops and improve the speed? Thank you all in advance.
You can do something as simple as :
img._data = ptr;
But beware, the ordering of values in the pixel buffer may be different from what you have in the input buffer, that could result in a strange looking image.
Now there is constructor that can use pointer as internal storage.
ex.
cimg_library::CImg<unsigned char> img(ptr, 640, 480, 1, 3, true);
It's important to set last parameter to true as it's indicate that the pointer ptr should be used as internal storage instead of copying values from pointer.
more info here:
http://cimg.eu/reference/structcimg__library_1_1CImg.html#a24f3b43daa4444b94a973c2c3fff82c5
Finally I decide to switch to opencv. In this way, the pointer can be easily assigned to the image mat and then be stored in an image.
I am still searching for the solution of cimg. It seems a bit strange to me that the cimg's pointer cannot be modified.
from https://devolio.net/fish2000/cimg-fltkhpp/ :
/* Generates a w*h*4 CImg<uchar> image from the given rgb data. */
static inline cimg_library::CImg<uchar> cimg_from_rgba(
uchar const* rgb,
const uint dimw, const uint dimh
)
{
cimg_library::CImg<uchar> res(dimw,dimh,1,4);
uchar *pR = res.data(0,0,0,0),
*pG = res.data(0,0,0,1),
*pB = res.data(0,0,0,2),
*pA = res.data(0,0,0,3);
const uchar *ptrs = rgb;
for ( uint off = dimw*dimh; off>0; --off )
{
*(pR++) = (uchar)*(ptrs++);
*(pG++) = (uchar)*(ptrs++);
*(pB++) = (uchar)*(ptrs++);
*(pA++) = (uchar)*(ptrs++);
}
return res;
}