• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2 
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6 
7     http://www.apache.org/licenses/LICENSE-2.0
8 
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 
16 #include "tensorflow/core/framework/op.h"
17 #include "tensorflow/core/framework/op_kernel.h"
18 #include "tensorflow/core/framework/shape_inference.h"
19 
20 namespace tensorflow {
21 
22 template <typename T>
23 class SingleImageRandomDotStereogramsOp : public OpKernel {
24  private:
25   int E2Epixels;  // Pixels from eye to eye = eye_to_eye_inches * DPI
26 
27   int input_Xvalue;  // X value of input Z values (width)
28   int input_Yvalue;  // Y value of input Z values (height)
29 
30   int output_Ximage;  // X value of output image (width)
31   int output_Yimage;  // Y value of output image (height)
32   int output_Cimage;  // color value of output image (color, 1 or 3)  (3 not
33                       // implemented)
34 
35   int data_box_left;    // X starting value for DATA window
36   int data_box_top;     // Y starting value for DATA window
37   int data_box_width;   // width of scan line
38   int data_box_height;  // hight of image
39 
40   int converge_dot_box_end;  // Row convergences dots end on
41 
42   uint8* outputImage;  // Output Image flat as a buffer (Tensor Connection)
43   double* ZBuffer;     // For internal use, allow for MASK, etc later, actual Z
44                        // used for Stereogram, XxY (X is the row index, y is col
45                        // index like a screen)
46                        // 0 (far) -> 1.0(near) range
47   bool hidden_surface_removal;
48   int convergence_dots_size;
49   int dots_per_inch;
50   float eye_separation;
51   float mu;
52   bool normalize;
53   float normalize_max;
54   float normalize_min;
55   float border_level;
56   int number_colors;
57   ::tensorflow::PartialTensorShape output_image_shape;
58   ::tensorflow::PartialTensorShape output_data_window;
59 
60   uint8 Cblack = 0;
61   uint8 Cwhite = 255;
62 
63   int indexMode = 0;  // 0 - truncate XY, 1 - round XY, 2 - Interpolate XY (not
64                       // implemented yet, keep default of 0)
65   int interp_x, interp_y;  // 1 - yes, 0 - no  interpolation directions (not
66                            // implemented yet)
67 
68   bool debugging = false;
69 
separation(double z)70   inline int separation(double z) {
71     return (std::round((1 - mu * z) * E2Epixels / (2 - mu * z)));
72   }
73 
get_far_width()74   inline int get_far_width() { return (separation(0.0)); }
get_near_width()75   inline int get_near_width() { return (separation(1.0)); }
76 
77  public:
SingleImageRandomDotStereogramsOp(OpKernelConstruction * context)78   explicit SingleImageRandomDotStereogramsOp(OpKernelConstruction* context)
79       : OpKernel(context) {  // Constructor
80     OP_REQUIRES_OK(context, context->GetAttr("hidden_surface_removal",
81                                              &hidden_surface_removal));
82     OP_REQUIRES_OK(context, context->GetAttr("convergence_dots_size",
83                                              &convergence_dots_size));
84     OP_REQUIRES_OK(context, context->GetAttr("dots_per_inch", &dots_per_inch));
85     OP_REQUIRES_OK(context,
86                    context->GetAttr("eye_separation", &eye_separation));
87     OP_REQUIRES_OK(context, context->GetAttr("mu", &mu));
88     OP_REQUIRES_OK(context, context->GetAttr("normalize", &normalize));
89     OP_REQUIRES_OK(context, context->GetAttr("normalize_max", &normalize_max));
90     OP_REQUIRES_OK(context, context->GetAttr("normalize_min", &normalize_min));
91     OP_REQUIRES_OK(context, context->GetAttr("border_level", &border_level));
92     OP_REQUIRES_OK(context, context->GetAttr("number_colors", &number_colors));
93     OP_REQUIRES_OK(context,
94                    context->GetAttr("output_image_shape", &output_image_shape));
95     OP_REQUIRES_OK(context,
96                    context->GetAttr("output_data_window", &output_data_window));
97 
98     E2Epixels =
99         eye_separation * dots_per_inch;  // Initialize pixels from eye to eye
100   }
101 
~SingleImageRandomDotStereogramsOp()102   ~SingleImageRandomDotStereogramsOp() override {  // Destructor
103   }
104 
Compute(OpKernelContext * context)105   void Compute(OpKernelContext* context) override {
106     const Tensor& input_tensor = context->input(0);
107     input_Xvalue = input_tensor.shape().dim_size(
108         1);  // X value is the number of columns of the input matrix
109     input_Yvalue =
110         input_tensor.shape().dim_size(0);  // Y value is the number of rows
111 
112     output_Ximage = output_image_shape.dim_size(0);
113     output_Yimage = output_image_shape.dim_size(1);
114     output_Cimage = output_image_shape.dim_size(2);
115 
116     if (number_colors > 256)  // Go to full color image
117       output_Cimage = 3;
118 
119     int data_Xwindow = output_data_window.dim_size(0);
120     int data_Ywindow = output_data_window.dim_size(1);
121 
122     int deltaX_border_image = output_Ximage - data_Xwindow;
123     int deltaY_border_image = output_Yimage - data_Ywindow;
124 
125     if (convergence_dots_size >
126         0)  // 3 frame sections in Y direction due to DOTS
127     {
128       deltaY_border_image =
129           deltaY_border_image -
130           convergence_dots_size;  // Take off space for Convergence Dots
131       deltaY_border_image = std::max(0, deltaY_border_image);
132       data_box_top = deltaY_border_image / 3;
133 
134       if (deltaY_border_image >= 0) {
135         converge_dot_box_end = output_Yimage - 1 - data_box_top;
136       } else {
137         converge_dot_box_end = output_Yimage - 1;
138       }
139     } else  // Otherwise only 2, no convergence dot
140     {
141       data_box_top = deltaY_border_image / 2;  // Center DATA in Y dimension
142       converge_dot_box_end = output_Yimage - 1;
143     }
144 
145     data_box_left = deltaX_border_image / 2;  // Center DATA in X dimension
146     data_box_width = data_Xwindow;            // width of scan line
147     data_box_height = data_Ywindow;           // hight of image
148 
149     const T* inputZ = input_tensor.flat<T>().data();  // Flatten input Z buffer
150 
151     BuildZBuffer(inputZ);
152 
153     // Output a scalar string.
154     Tensor* output_tensor = nullptr;
155     OP_REQUIRES_OK(
156         context,
157         context->allocate_output(
158             0, TensorShape({output_Yimage, output_Ximage, output_Cimage}),
159             &output_tensor));
160 
161     outputImage = output_tensor->flat<uint8>().data();
162 
163     generate_stereogram();
164 
165     delete[] ZBuffer;
166   }
167 
168   //***************************************************************************
169   //***************************************************************************
170   // Move input into standard Z format to reduce complexity of algorithm
171   //
BuildZBuffer(const T * Z,bool log=false)172   void BuildZBuffer(const T* Z, bool log = false) {
173     double MaxValue = 1.0;
174     double MinValue = 0.0;
175     ZBuffer = new double[input_Xvalue * input_Yvalue];  // Used to computer
176                                                         // final Z values before
177                                                         // rendering to output
178 
179     if (normalize) {
180       // Init Min/Max to first value
181       if (normalize_max < normalize_min)  // Autoscale if MIN>MAX
182       {
183         MaxValue = *Z;
184         MinValue = *Z;
185 
186         for (int y = 0; y < input_Yvalue; ++y)
187           for (int x = 0; x < input_Xvalue; ++x) {
188             double value = getZfromInputImage(Z, x, y);
189             if (value > MaxValue) MaxValue = value;
190             if (value < MinValue) MinValue = value;
191           }
192       } else {
193         MaxValue = normalize_max;
194         MinValue = normalize_min;
195       }
196     }
197 
198     for (int y = 0; y < input_Yvalue; ++y)
199       for (int x = 0; x < input_Xvalue; ++x) {
200         double value = getZfromInputImage(Z, x, y);
201 
202         if (normalize) {
203           value = (value - MinValue) / (MaxValue - MinValue);
204         }
205 
206         if (value > 1.0) value = 1.0;
207         if (value < 0.0) value = 0.0;
208 
209         *(ZBuffer + (input_Xvalue * y + x)) = value;
210       }
211   }
212 
213   //***************************************************************************
214   //***************************************************************************
getZfromInputImage(const T * Z,int x,int y)215   double getZfromInputImage(const T* Z, int x, int y) {
216     return *(Z + input_Xvalue * y + x);
217   }
218 
219   //***************************************************************************
220   //***************************************************************************
221   // All normalized, not checking required
222   // Possible Projection issue if DATA is bigger or smaller than Input
223   //  Modes include:
224   //         Truncate value (Default)
225   //         Round-off value
226   //         Interpolate between values
227   //
getZfromZbuffer(double x,double y)228   double getZfromZbuffer(double x, double y) {
229     int xi, yi;
230 
231     switch (indexMode) {
232       case 0:  // Truncate
233         xi = int(x);
234         yi = int(y);
235         return (*(ZBuffer + (xi + input_Xvalue * yi)));
236         break;
237       case 1:  // Round-off
238         xi = std::round(x);
239         yi = std::round(y);
240         return (*(ZBuffer + (xi + input_Xvalue * yi)));
241         break;
242       case 2:  // Interpolate (Not implemented yet, will need 4 points
243                // [x,y],[x+1,y],[x,y+1],[x+1,y+1], then interpolate)
244         xi = int(x);
245         yi = int(y);
246         return (*(ZBuffer + (xi + input_Xvalue * yi)));
247         break;
248       default:  // Round-off is the default
249         xi = int(x + 0.5);
250         yi = int(y + 0.5);
251         return (*(ZBuffer + (xi + input_Xvalue * yi)));
252         break;
253     }
254   }
255 
256   //***************************************************************************
257   //***************************************************************************
258 
getOutputImageIndex(int x,int y,int channel)259   int getOutputImageIndex(int x, int y,
260                           int channel) {  // No error checking for some
261                                           // optimization, calling routine
262                                           // required to make sure there is no
263                                           // violation
264     return ((output_Ximage * output_Cimage) * y + x * output_Cimage + channel);
265   }
266 
267   //***************************************************************************
268   //***************************************************************************
269 
getZFromOutputPixel(int x,int y)270   double getZFromOutputPixel(int x, int y) {
271     // Convert pixel units to Z units, do this as "double"
272     double xofz = static_cast<double>(input_Xvalue) * (x - data_box_left) /
273                   (static_cast<double>(data_box_width));
274     double yofz = static_cast<double>(input_Yvalue) * (y - data_box_top) /
275                   (static_cast<double>(data_box_height));
276 
277     if ((xofz < 0) || (yofz < 0) || (yofz >= input_Yvalue) ||
278         (xofz >= input_Xvalue)) {  // Top of left side border hit or Right
279                                    // side or bottom border hit,
280                                    // send BORDER Z value
281       return border_level;
282     } else {
283       return getZfromZbuffer(xofz, yofz);
284     }
285   }
286 
287   //***************************************************************************
288   //***************************************************************************
289 
generate_stereogram()290   void generate_stereogram() {
291     int s, left, right, visible, t, l;
292     double zt, gz;
293     // Scan line
294     uint8* pix;  // Scan row color for each pixel
295     int* same;   // Used to determine if Pixel needs to be the same as another
296                  // pixel in the row
297 
298     pix = new uint8[output_Ximage * output_Cimage];
299     same = new int[output_Ximage];
300 
301     for (int y = 0; y < output_Yimage; ++y) {
302       // Set no dependencies on any pixels, tie each one back to itself
303       for (int x = 0; x < output_Ximage; ++x) same[x] = x;
304 
305       for (int x = 0; x < output_Ximage; ++x) {
306         gz = getZFromOutputPixel(x, y);
307         s = separation(gz);
308         left = x - s / 2;
309         right = left + s;
310 
311         if ((left >= 0) && (right < output_Ximage)) {
312           t = 1;
313           visible = 1;
314           if (hidden_surface_removal) do {
315               zt = gz + 2 * (2 - mu * gz) * t / (mu * E2Epixels);
316               visible = (getZFromOutputPixel(x - t, y) < zt) &&
317                         (getZFromOutputPixel(x + t, y) < zt);
318               ++t;
319             } while ((visible) && (zt < 1));
320 
321           if (visible) {
322             l = same[left];
323             while ((l != left) && (l != right))
324               if (l < right) {
325                 left = l;
326                 l = same[left];
327               } else {
328                 same[left] = right;
329                 left = right;
330                 l = same[left];
331                 right = l;
332               }
333             same[left] = right;
334           }
335         }
336       }
337       // Set colors for scan row, use channels and number_colors
338       for (int x = output_Ximage - 1; x >= 0; x--) {
339         for (int channel = 0; channel < output_Cimage; ++channel) {
340           if (same[x] == x) {  // Pick a random color
341             if (number_colors == 2) {
342               if ((rand() % 2) == 0) {
343                 pix[x * output_Cimage + channel] = Cblack;
344               } else {
345                 pix[x * output_Cimage + channel] = Cwhite;
346               }
347             } else {
348               pix[x * output_Cimage + channel] = rand() % 256;
349             }
350           } else
351             pix[x * output_Cimage + channel] =
352                 pix[same[x] * output_Cimage + channel];
353 
354           setpixel(x, y, channel, pix[x * output_Cimage + channel]);
355         }
356       }
357     }
358 
359     draw_convergence_dots();
360 
361     delete[] pix;
362     delete[] same;
363   }
364 
365   //***************************************************************************
366   //***************************************************************************
367 
draw_convergence_dots()368   void draw_convergence_dots() {
369     int x1, x2;  // center position for convergence dots
370 
371     if (convergence_dots_size == 0)  // No dot, return
372       return;
373 
374     x1 = output_Ximage / 2 - get_far_width() / 2;
375     x2 = output_Ximage / 2 + get_far_width() / 2;
376 
377     for (int lloop = 0; lloop < convergence_dots_size; ++lloop)
378       for (int wloop = 0; wloop < convergence_dots_size; ++wloop)
379         for (int channel = 0; channel < output_Cimage; ++channel) {
380           setpixel(x1 - (convergence_dots_size / 2) + wloop,
381                    converge_dot_box_end - lloop, channel, Cblack);
382           setpixel(x2 - (convergence_dots_size / 2) + wloop,
383                    converge_dot_box_end - lloop, channel, Cblack);
384         }
385   }
386 
387   //***************************************************************************
388   //***************************************************************************
389 
setpixel(int x,int y,int channel,uint8 color)390   void setpixel(int x, int y, int channel, uint8 color) {
391     *(outputImage + getOutputImageIndex(x, y, channel)) = color;
392   }
393 };
394 
395 #define REGISTER_KERNEL(T)                                        \
396   REGISTER_KERNEL_BUILDER(Name("SingleImageRandomDotStereograms") \
397                               .Device(DEVICE_CPU)                 \
398                               .TypeConstraint<T>("T"),            \
399                           SingleImageRandomDotStereogramsOp<T>);
400 
401 REGISTER_KERNEL(int32);
402 REGISTER_KERNEL(int64);
403 REGISTER_KERNEL(float);
404 REGISTER_KERNEL(double);
405 
406 #undef REGISTER_KERNEL
407 
408 }  // end namespace tensorflow
409