ofxDepthStream
src/ofxDepthStream/functional.h
Go to the documentation of this file.
1 //
2 // This file is part of the ofxDepthStream [https://github.com/fusefactory/ofxDepthStream]
3 // Copyright (C) 2018 Fuse srl
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 
21 #pragma once
22 
23 // OF
24 #include "ofMain.h"
25 // local
26 #include "DepthStream.h"
27 
31 namespace ofxDepthStream {
32 
33  static const size_t FRAME_SIZE_640x480x16BIT = (640*480*2); // orbbec
34  static const size_t FRAME_SIZE_640x480x32BIT = (640*480*4);
35  static const size_t FRAME_SIZE_512x424x32BIT = (512*424*4); // kinect
36 
37  // Depth texture loader methods // // // // //
38 
40  struct DepthLoaderOpts {
41  int minDistance=0;
42  int maxDistance=5000;
43  int vertCorrection=0; // 1?
44  int shift1=0, shift2=0;
45  float keystone=0.0f;
46  float margins[4]={0.0f, 0.0f, 0.0f, 0.0f};
47 
48  DepthLoaderOpts& setMinDistance(int v) { minDistance = v; return *this; }
49  DepthLoaderOpts& setMaxDistance(int v) { maxDistance = v; return *this; }
50  DepthLoaderOpts& setVertCorrection(int v) { vertCorrection = v; return *this; }
51  DepthLoaderOpts& setMargins(const float* v) { margins[0] = v[0]; margins[1] = v[1]; margins[2] = v[2]; margins[3] = v[3]; return *this; }
52  DepthLoaderOpts& setMarginTop(float v) { margins[0] = v; return *this; }
53  DepthLoaderOpts& setMarginRight(float v) { margins[1] = v; return *this; }
54  DepthLoaderOpts& setMarginBottom(float v) { margins[2] = v; return *this; }
55  DepthLoaderOpts& setMarginLeft(float v) { margins[3] = v; return *this; }
56  DepthLoaderOpts& setKeystone(float v) { keystone = v; return *this; }
57  DepthLoaderOpts& setShift1(int v) { shift1 = v; return *this; }
58  DepthLoaderOpts& setShift2(int v) { shift2 = v; return *this; }
59  };
60 
64  void loadDepthTexture16bit(ofTexture& tex, const void* data, size_t size, const DepthLoaderOpts& opts = DepthLoaderOpts()) {
65  // loadEdgeData based on KinectRemote::newData method in the Dokk_OF repo (but converted to 16-bit)
66 
67  // allocate
68  if(!tex.isAllocated()) {
69  // ofLogNotice() << "Allocating edge-data texture";
70  if(size == FRAME_SIZE_640x480x16BIT) {
71  tex.allocate(640, 480, GL_RGB);
72  } else {
73  ofLogWarning() << "Frame-size not supported by ofxDepthStream::loadDepthTexture16bit: " << size;
74  return;
75  }
76  }
77 
78  // // check
79  // size_t expectedSize = tex.getWidth() * tex.getHeight() * 2;
80  // if(expectedSize != size) {
81  // ofLogWarning() << "Edge-data texture size did not match data-size (got: " << size << ", expected: " << expectedSize << ")";
82  // return;
83  // }
84 
85  // 3-channel data buffer
86  float edgeData[(int)tex.getWidth() * (int)tex.getHeight() * 3];
87 
88  for (int y = 0.0; y < tex.getHeight(); y++) {
89  for (int x = 0.0; x < tex.getWidth(); x++) {
90  // keystone
91  int posX = x + ((y - tex.getHeight() / 2.0) / (tex.getHeight() / 2.0)) * opts.keystone * (x - tex.getWidth() / 2.0);
92 
93  // convert multi-byte into single depth value
94  int depthIndex = int((posX + y * tex.getWidth()) * 2);
95  // unsigned int byte0 = ((unsigned char*)data)[depthIndex + 0];
96  // unsigned int byte1 = ((unsigned char*)data)[depthIndex + 1];
97  // unsigned int depth = ((byte0 & 0xFF) << (8 + opts.shift1)) | ((byte1 & 0xFF) << opts.shift2);
98  uint16_t depth = *((uint16_t*)(&((char*)data)[depthIndex]));
99  int edgeIndex = (x + y * tex.getWidth());
100  bool valid = false;
101 
102  if (
103  // top margin
104  y >= opts.margins[0]
105  // left margin
106  && posX >= opts.margins[3]
107  // right margin
108  && posX <= tex.getWidth() - opts.margins[1]
109  // bottom margin
110  && y <= tex.getHeight() - opts.margins[2])
111  {
112  int correctMaxDistance = opts.maxDistance * (1.0 - opts.vertCorrection * (std::cos(M_PI / 3.0 * (tex.getHeight() - y) / tex.getHeight()) - 0.5));
113  if (depth >= opts.minDistance && depth <= correctMaxDistance) {
114  float intensity = (depth - opts.minDistance) / (float)(correctMaxDistance - opts.minDistance);
115  edgeData[edgeIndex * 3 + 0] = 1 - intensity;
116  edgeData[edgeIndex * 3 + 1] = 1 - intensity;
117  edgeData[edgeIndex * 3 + 2] = 1 - intensity;
118  valid = true;
119  }
120  }
121  if (!valid) {
122  edgeData[edgeIndex * 3 + 0] = 0.0;
123  edgeData[edgeIndex * 3 + 1] = 0.0;
124  edgeData[edgeIndex * 3 + 2] = 0.0;
125  }
126  }
127  }
128 
129  tex.loadData((float*)edgeData, tex.getWidth(), tex.getHeight(), GL_RGB);
130  // ofLogNotice() << "loaded edge data texture";//: maxFoundDist: " << maxFoundDist;
131  }
132 
136  void loadDepthTexture32bit(ofTexture& tex, const void* data, size_t size, const DepthLoaderOpts& opts = DepthLoaderOpts()) {
137  // loadEdgeData based on KinectRemote::newData method in the Dokk_OF repo
138 
139  // allocate
140  if(!tex.isAllocated()) {
141  // ofLogNotice() << "Allocating edge-data texture";
142  if(size == FRAME_SIZE_640x480x32BIT) {
143  tex.allocate(640, 480, GL_RGB);
144  } else if(size == FRAME_SIZE_512x424x32BIT){
145  tex.allocate(512, 424, GL_RGB);
146  } else {
147  ofLogWarning() << "Frame-size not supported by ofxDepthStream::loadDepthTexture32bit: " << size;
148  return;
149  }
150  }
151 
152  // // check
153  // size_t expectedSize = tex.getWidth() * tex.getHeight() * 2;
154  // if(expectedSize != size) {
155  // ofLogWarning() << "Edge-data texture size did not match data-size (got: " << size << ", expected: " << expectedSize << ")";
156  // return;
157  // }
158 
159  // 3-channel data buffer
160  float edgeData[(int)tex.getWidth() * (int)tex.getHeight() * 3];
161 
162  for (int y = 0.0; y < tex.getHeight(); y++) {
163  for (int x = 0.0; x < tex.getWidth(); x++) {
164  // keystone
165  int posX = x + ((y - tex.getHeight() / 2.0) / (tex.getHeight() / 2.0)) * opts.keystone * (x - tex.getWidth() / 2.0);
166 
167  // convert multi-byte into single depth value
168  int depthIndex = int((posX + y * tex.getWidth()) * 4);
169  int byte0 = ((unsigned char*)data)[depthIndex + 0];
170  int byte1 = ((unsigned char*)data)[depthIndex + 1];
171  int byte2 = ((unsigned char*)data)[depthIndex + 2];
172  int byte3 = ((unsigned char*)data)[depthIndex + 3];
173  int depth = byte0 << 24 | (byte1 & 0xFF) << 16 | (byte2 & 0xFF) << 8 | (byte3 & 0xFF);
174 
175  int edgeIndex = (x + y * tex.getWidth());
176  bool valid = false;
177 
178  if (
179  // top margin
180  y >= opts.margins[0]
181  // left margin
182  && posX >= opts.margins[3]
183  // right margin
184  && posX <= tex.getWidth() - opts.margins[1]
185  // bottom margin
186  && y <= tex.getHeight() - opts.margins[2])
187  {
188  int correctMaxDistance = opts.maxDistance * (1.0 - opts.vertCorrection * (std::cos(M_PI / 3.0 * (tex.getHeight() - y) / tex.getHeight()) - 0.5));
189  if (depth >= opts.minDistance && depth <= correctMaxDistance) {
190  float intensity = (depth - opts.minDistance) / (float)(correctMaxDistance - opts.minDistance);
191  edgeData[edgeIndex * 3 + 0] = 1 - intensity;
192  edgeData[edgeIndex * 3 + 1] = 1 - intensity;
193  edgeData[edgeIndex * 3 + 2] = 1 - intensity;
194  valid = true;
195  }
196  }
197  if (!valid) {
198  edgeData[edgeIndex * 3 + 0] = 0.0;
199  edgeData[edgeIndex * 3 + 1] = 0.0;
200  edgeData[edgeIndex * 3 + 2] = 0.0;
201  }
202  }
203  }
204 
205  tex.loadData((float*)edgeData, tex.getWidth(), tex.getHeight(), GL_RGB);
206  // ofLogNotice() << "loaded edge data texture";//: maxFoundDist: " << maxFoundDist;
207  }
208 
212  void loadDepthTexture(ofTexture& tex, const void* data, size_t size, const DepthLoaderOpts& opts = DepthLoaderOpts()) {
213  if (size == FRAME_SIZE_640x480x16BIT) {
214  loadDepthTexture16bit(tex, data, size, opts);
215  return;
216  }
217 
218  if (size == FRAME_SIZE_640x480x32BIT || size == FRAME_SIZE_512x424x32BIT) {
219  loadDepthTexture32bit(tex, data, size, opts);
220  return;
221  }
222 
223  ofLogWarning() << "Frame size not supported by ofxDepthStream::loadDepthTexture (bytes): " << size;
224  }
225 
231  void loadDepthTexture(depth::Buffer& buffer, ofTexture& tex, const DepthLoaderOpts& opts = DepthLoaderOpts()) {
232  // check if buffer has data
233  depth::emptyAndInflateBuffer(buffer, [&tex, &opts](const void* data, size_t size){
234  loadDepthTexture(tex, data, size, opts);
235  });
236  }
237 
238  // Mesh Loader methods // // // // //
239 
240  struct MeshLoaderOpts {
241  float depthFactor=-1.0f;
242  MeshLoaderOpts& setDepthFactor(float v) { depthFactor = v; return *this; }
243  };
244 
248  void loadMesh16bit(ofMesh& mesh, const void* data, size_t width, size_t height, const MeshLoaderOpts& opts = MeshLoaderOpts()) {
249  const uint16_t* pointData = (const uint16_t*)data;
250 
251  mesh.clear();
252 
253  for (int y = 0; y < height; y++) {
254  for (int x = 0; x < width; x++) {
255  uint16_t val = pointData[y*width+x];
256  ofVec3f p(x,y, val * opts.depthFactor);
257  mesh.addVertex(p);
258 
259  // TODO; provide some coloring options
260  float hue = ofMap(val, 0, 6000, 0, 255);
261  mesh.addColor(ofColor::fromHsb(hue, 255, 255));
262  }
263  }
264  }
265 
269  void loadMesh32bit(ofMesh& mesh, const void* data, size_t width, size_t height, const MeshLoaderOpts& opts = MeshLoaderOpts()) {
270  const uint32_t* pointData = (const uint32_t*)data;
271 
272  mesh.clear();
273 
274  for (int y = 0; y < height; y++) {
275  for (int x = 0; x < width; x++) {
276  uint32_t val = pointData[y*width+x]; // TODO/TOTEST; do manual byte processing here?
277  ofVec3f p(x,y, val * opts.depthFactor);
278  mesh.addVertex(p);
279 
280  // TODO; provide some coloring options
281  float hue = ofMap(val, 0, 6000, 0, 255);
282  mesh.addColor(ofColor::fromHsb(hue, 255, 255));
283  }
284  }
285  }
286 
292  void loadMesh(ofMesh& mesh, const void* data, size_t size, const MeshLoaderOpts& opts = MeshLoaderOpts()) {
293  if(size == FRAME_SIZE_640x480x16BIT) {
294  loadMesh16bit(mesh, data, 640, 480, opts);
295  return;
296  }
297 
298  if(size == FRAME_SIZE_640x480x32BIT) {
299  loadMesh32bit(mesh, data, 640, 480, opts);
300  return;
301  }
302 
303  if(size == FRAME_SIZE_512x424x32BIT){
304  loadMesh32bit(mesh, data, 512, 424, opts);
305  return;
306  }
307 
308  ofLogWarning() << "Frame size not supported by ofxDepthStream::loadMesh (bytes): " << size;
309  }
310 
317  void loadMesh(depth::Buffer& buffer, ofMesh& mesh, const MeshLoaderOpts& opts = MeshLoaderOpts()) {
318  // check if buffer has data
319  depth::emptyAndInflateBuffer(buffer, [&mesh, &opts](const void* data, size_t size){
320  loadMesh(mesh, data, size, opts);
321  });
322  }
323 
324  // Color texture loader methods (untested) // // // // //
325 
329  void loadColorTexture(ofTexture& tex, const void* data, size_t size) {
330  ofPixels pixels;
331 
332  // allocate
333  if(tex.isAllocated()) {
334  pixels.allocate(tex.getWidth(), tex.getHeight(), OF_IMAGE_COLOR);
335  } else {
336  ofLogWarning() << "TODO: infer color texture resolution from data-size";
337  pixels.allocate(1280, 720, OF_IMAGE_COLOR);
338  tex.allocate(pixels);
339  }
340 
341  // check
342  if((tex.getWidth() * tex.getHeight()) * 3 != size) {
343  ofLogWarning() << "Color texture size did not match data-size (got: " << size << ", expected: 1280x720x3=2764800)";
344  return;
345  }
346 
347  // load
348  pixels.setFromPixels((const unsigned char *)data, pixels.getWidth(), pixels.getHeight(), OF_IMAGE_COLOR);
349  tex.loadData(pixels);
350  }
351 
358  void loadColorTexture(depth::Buffer& buffer, ofTexture& tex) {
359  // check if buffer has data
360  depth::emptyAndInflateBuffer(buffer, [&tex](const void* data, size_t size){
361  loadColorTexture(tex, data, size);
362  });
363  }
364 }
int minDistance
Definition: src/ofxDepthStream/functional.h:41
float depthFactor
Definition: src/ofxDepthStream/functional.h:241
MeshLoaderOpts & setDepthFactor(float v)
Definition: src/ofxDepthStream/functional.h:242
Definition: src/ofxDepthStream/functional.h:240
Definition: src/ofxDepthStream/functional.h:31
DepthLoaderOpts & setMinDistance(int v)
Definition: src/ofxDepthStream/functional.h:48
void loadDepthTexture32bit(ofTexture &tex, const void *data, size_t size, const DepthLoaderOpts &opts=DepthLoaderOpts())
populates the texture instance with 32-bit grayscale depth-image data from the given frame-data...
Definition: src/ofxDepthStream/functional.h:136
float margins[4]
Definition: src/ofxDepthStream/functional.h:46
DepthLoaderOpts & setKeystone(float v)
Definition: src/ofxDepthStream/functional.h:56
DepthLoaderOpts & setShift1(int v)
Definition: src/ofxDepthStream/functional.h:57
DepthLoaderOpts & setMarginBottom(float v)
Definition: src/ofxDepthStream/functional.h:54
contains all classes and functions of the DepthStream library.
Definition: Buffer.h:22
DepthLoaderOpts & setMaxDistance(int v)
Definition: src/ofxDepthStream/functional.h:49
DepthLoaderOpts & setMarginTop(float v)
Definition: src/ofxDepthStream/functional.h:52
void emptyAndInflateBuffer(Buffer &buf, Frame::InputFunc func)
Definition: libs/DepthStream/src/functional.h:140
int maxDistance
Definition: src/ofxDepthStream/functional.h:42
int shift1
Definition: src/ofxDepthStream/functional.h:44
DepthLoaderOpts & setShift2(int v)
Definition: src/ofxDepthStream/functional.h:58
void loadDepthTexture(ofTexture &tex, const void *data, size_t size, const DepthLoaderOpts &opts=DepthLoaderOpts())
populates the texture instance with grayscale depth-image data from the given frame-data.
Definition: src/ofxDepthStream/functional.h:212
void loadMesh(ofMesh &mesh, const void *data, size_t size, const MeshLoaderOpts &opts=MeshLoaderOpts())
populates the mesh instance with depth-image data.
Definition: src/ofxDepthStream/functional.h:292
void loadDepthTexture16bit(ofTexture &tex, const void *data, size_t size, const DepthLoaderOpts &opts=DepthLoaderOpts())
populates the texture instance with 16-bit grayscale depth-image data from the given frame-data...
Definition: src/ofxDepthStream/functional.h:64
void loadMesh16bit(ofMesh &mesh, const void *data, size_t width, size_t height, const MeshLoaderOpts &opts=MeshLoaderOpts())
populates the mesh instance with 16-bit depth-image data.
Definition: src/ofxDepthStream/functional.h:248
DepthLoaderOpts & setMarginRight(float v)
Definition: src/ofxDepthStream/functional.h:53
float keystone
Definition: src/ofxDepthStream/functional.h:45
DepthLoaderOpts & setVertCorrection(int v)
Definition: src/ofxDepthStream/functional.h:50
DepthLoaderOpts & setMarginLeft(float v)
Definition: src/ofxDepthStream/functional.h:55
DepthLoaderOpts & setMargins(const float *v)
Definition: src/ofxDepthStream/functional.h:51
int vertCorrection
Definition: src/ofxDepthStream/functional.h:43
int shift2
Definition: src/ofxDepthStream/functional.h:44
void loadColorTexture(ofTexture &tex, const void *data, size_t size)
UNTESTED populates the texture with color-image data from the given frame-data.
Definition: src/ofxDepthStream/functional.h:329
Manages a reference to a Frame instance.
Definition: Buffer.h:29
Options container for the loadDepth* methods.
Definition: src/ofxDepthStream/functional.h:40
void loadMesh32bit(ofMesh &mesh, const void *data, size_t width, size_t height, const MeshLoaderOpts &opts=MeshLoaderOpts())
UNTESTED populates the mesh instance with 32-bit depth-image data.
Definition: src/ofxDepthStream/functional.h:269