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