1 // Copyright (C) 2020 by Yuri Victorovich. All rights reserved.
2 
3 #include "misc.h"
4 #include "tensor.h"
5 
6 #include <functional>
7 #include <fstream>
8 #include <limits>
9 #include <memory>
10 #include <streambuf>
11 #include <string>
12 
13 #include <assert.h>
14 #include <nlohmann/json.hpp>
15 
16 namespace Tensor {
17 
flatSize(const TensorShape & shape)18 size_t flatSize(const TensorShape &shape) {
19 	size_t sz = 1;
20 	for (auto d : shape)
21 		sz *= d;
22 	return sz;
23 }
24 
sizeBetweenDims(const TensorShape & shape,int dim1,int dim2)25 size_t sizeBetweenDims(const TensorShape &shape, int dim1, int dim2) {
26 	size_t sz = 1;
27 	for (int d = dim1; d <= dim2; d++)
28 		sz *= shape[d];
29 	return sz;
30 }
31 
numMultiDims(const TensorShape & shape)32 unsigned numMultiDims(const TensorShape &shape) {
33 	unsigned numMultiDims = 0;
34 	for (auto d : shape)
35 		if (d > 1)
36 			numMultiDims++;
37 	return numMultiDims;
38 }
39 
getLastDims(const TensorShape & shape,unsigned ndims)40 TensorShape getLastDims(const TensorShape &shape, unsigned ndims) {
41 	TensorShape s = shape;
42 	while (s.size() > ndims)
43 		s.erase(s.begin());
44 	return s;
45 }
46 
stripLeadingOnes(const TensorShape & shape)47 TensorShape stripLeadingOnes(const TensorShape &shape) {
48 	TensorShape s = shape;
49 	while (!s.empty() && s[0]==1)
50 		s.erase(s.begin());
51 	return s;
52 }
53 
isSubset(const TensorShape & shapeLarge,const TensorShape & shapeSmall)54 bool isSubset(const TensorShape &shapeLarge, const TensorShape &shapeSmall) {
55 	// check size
56 	if (shapeLarge.size() < shapeSmall.size())
57 		return false;
58 
59 	// strip ones
60 	TensorShape small = shapeSmall;
61 	while (!small.empty() && small[0]==1)
62 		small.erase(small.begin());
63 
64 	// compare
65 	for (TensorShape::const_reverse_iterator it = small.rbegin(), ite = small.rend(), itl = shapeLarge.rbegin(); it!=ite; it++, itl++)
66 		if (*it != *itl)
67 			return false;
68 
69 	// all matched
70 	return true;
71 }
72 
computeArgMax(const TensorShape & inputShape,const float * input,const std::vector<float> & palette)73 float* computeArgMax(const TensorShape &inputShape, const float *input, const std::vector<float> &palette) {
74 	assert(palette.size()%3 == 0); // it's a color palette with {R,G,B}
75 	assert(*inputShape.rbegin() == palette.size()/3); // match the number of colors
76 
77 	auto inputSize = flatSize(inputShape);
78 	auto nchannels = *inputShape.rbegin();
79 	std::unique_ptr<float> output(new float[inputSize/nchannels*3]);
80 	float *o = output.get();
81 
82 	for (auto inpute = input+inputSize; input<inpute; ) {
83 		auto nc = nchannels;
84 		unsigned c = 0;
85 		float val = std::numeric_limits<float>::lowest();
86 		int maxChannel = -1;
87 		do {
88 			if (*input > val) {
89 				val = *input;
90 				maxChannel = c;
91 			}
92 			input++;
93 			c++;
94 		} while (--nc > 0);
95 
96 		const float *p = &palette[maxChannel*3];
97 		*o++ = *p++;
98 		*o++ = *p++;
99 		*o++ = *p;
100 	}
101 
102 	return output.release();
103 }
104 
canBeAnImage(const TensorShape & shape)105 bool canBeAnImage(const TensorShape &shape) {
106 	return shape.size() == 3/*HWC*/ && (shape[2]==3 || shape[2]==1);
107 }
108 
saveTensorDataAsJson(const TensorShape & shape,const float * data,const char * fileName)109 void saveTensorDataAsJson(const TensorShape &shape, const float *data, const char *fileName) {
110 	using json = nlohmann::json;
111 
112 	std::function<json(unsigned,unsigned,const float*)> one;
113 	one = [&shape,&one](unsigned level, unsigned step, const float *data) {
114 		if (level < shape.size()) {
115 			json j = json::array();
116 			auto sz = shape[level];
117 			step /= sz;
118 			for (unsigned i = 0, ie = sz; i < ie; i++, data += step)
119 				j.push_back(one(level+1, step, data));
120 			return j;
121 		} else
122 			return json(*data);
123 	};
124 
125 	// convert to json
126 	json j = one(0, flatSize(shape), data);
127 
128 	// save
129 	std::ofstream f;
130 	f.open(fileName, std::ios_base::out|std::ios_base::trunc);
131 	if (!f.good()) {
132 		PRINT_ERR("failed to open the json file for writing")
133 		return;
134 	}
135 	f << j;
136 	f.close();
137 }
138 
readTensorDataAsJson(const char * fileName,const TensorShape & shape,std::shared_ptr<const float> & tensorData)139 bool readTensorDataAsJson(const char *fileName, const TensorShape &shape, std::shared_ptr<const float> &tensorData) {
140 	using json = nlohmann::json;
141 
142 	std::ifstream f(fileName);
143 	if (!f)
144 		return false;
145 
146 	std::string str((std::istreambuf_iterator<char>(f)),
147 	                 std::istreambuf_iterator<char>());
148 
149 	auto shapeSize = flatSize(shape);
150 	std::unique_ptr<float> data(new float[shapeSize]);
151 	float *p = data.get(), *pe = p + shapeSize;
152 
153 	std::function<bool(const json &j)> one;
154 	one = [&p,pe,&one](const json &j) {
155 		if (j.is_array()) {
156 			for (auto &e : j)
157 				if (!one(e))
158 					return false; // failed to import data
159 			return true;
160 		} else if (j.is_number()) {
161 			if (p == pe)
162 				return false; // no space for the next element: must be a shape mismatch
163 			*p++ = j.get<float>();
164 			return true;
165 		} else
166 			return false; // can't be any other JSON object type in the tensor data file
167 	};
168 
169 	if (one(json::parse(str)) && p == pe) {
170 		tensorData.reset(data.release());
171 		return true;
172 	}
173 
174 	return false;
175 
176 }
177 
178 }
179