aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/contrib/image/kernels/single_image_random_dot_stereograms_ops.cc
diff options
context:
space:
mode:
Diffstat (limited to 'tensorflow/contrib/image/kernels/single_image_random_dot_stereograms_ops.cc')
-rwxr-xr-xtensorflow/contrib/image/kernels/single_image_random_dot_stereograms_ops.cc424
1 files changed, 424 insertions, 0 deletions
diff --git a/tensorflow/contrib/image/kernels/single_image_random_dot_stereograms_ops.cc b/tensorflow/contrib/image/kernels/single_image_random_dot_stereograms_ops.cc
new file mode 100755
index 0000000000..23efd359d5
--- /dev/null
+++ b/tensorflow/contrib/image/kernels/single_image_random_dot_stereograms_ops.cc
@@ -0,0 +1,424 @@
+/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+==============================================================================*/
+
+#include "tensorflow/core/framework/op.h"
+#include "tensorflow/core/framework/op_kernel.h"
+#include "tensorflow/core/framework/shape_inference.h"
+
+namespace tensorflow {
+
+using shape_inference::InferenceContext;
+
+template <typename T>
+class SingleImageRandomDotStereogramsOp : public OpKernel {
+ private:
+ int E2Epixels; // Pixels from eye to eye = eye_to_eye_inches * DPI
+
+ int input_Xvalue; // X value of input Z values (width)
+ int input_Yvalue; // Y value of input Z values (height)
+
+ int output_Ximage; // X value of output image (width)
+ int output_Yimage; // Y value of output image (height)
+ int output_Cimage; // color value of output image (color, 1 or 3) (3 not
+ // implemented)
+
+ int data_box_left; // X starting value for DATA window
+ int data_box_top; // Y starting value for DATA window
+ int data_box_width; // width of scan line
+ int data_box_height; // hight of image
+
+ int converge_dot_box_end; // Row convergences dots end on
+
+ uint8* outputImage; // Output Image flat as a buffer (Tensor Connection)
+ double* ZBuffer; // For internal use, allow for MASK, etc later, actual Z
+ // used for Stereogram, XxY (X is the row index, y is col
+ // index like a screen)
+ // 0 (far) -> 1.0(near) range
+ bool hidden_surface_removal;
+ int convergence_dots_size;
+ int dots_per_inch;
+ float eye_separation;
+ float mu;
+ bool normalize;
+ float normalize_max;
+ float normalize_min;
+ float border_level;
+ int number_colors;
+ ::tensorflow::TensorShapeProto output_image_shape;
+ ::tensorflow::TensorShapeProto output_data_window;
+
+ uint8 Cblack = (uint8)0;
+ uint8 Cwhite = (uint8)255;
+
+ int indexMode = 0; // 0 - truncate XY, 1 - round XY, 2 - Interpolate XY (not
+ // implemented yet, keep default of 0)
+ int interp_x, interp_y; // 1 - yes, 0 - no interpolation directions (not
+ // implemented yet)
+
+ bool debugging = false;
+
+ inline int separation(double z) {
+ return (std::round((1 - mu * z) * E2Epixels / (2 - mu * z)));
+ }
+
+ inline int get_far_width() { return (separation(0.0)); }
+ inline int get_near_width() { return (separation(1.0)); }
+
+ public:
+ explicit SingleImageRandomDotStereogramsOp(OpKernelConstruction* context)
+ : OpKernel(context) { // Constructor
+ OP_REQUIRES_OK(context, context->GetAttr("hidden_surface_removal",
+ &hidden_surface_removal));
+ OP_REQUIRES_OK(context, context->GetAttr("convergence_dots_size",
+ &convergence_dots_size));
+ OP_REQUIRES_OK(context, context->GetAttr("dots_per_inch", &dots_per_inch));
+ OP_REQUIRES_OK(context,
+ context->GetAttr("eye_separation", &eye_separation));
+ OP_REQUIRES_OK(context, context->GetAttr("mu", &mu));
+ OP_REQUIRES_OK(context, context->GetAttr("normalize", &normalize));
+ OP_REQUIRES_OK(context, context->GetAttr("normalize_max", &normalize_max));
+ OP_REQUIRES_OK(context, context->GetAttr("normalize_min", &normalize_min));
+ OP_REQUIRES_OK(context, context->GetAttr("border_level", &border_level));
+ OP_REQUIRES_OK(context, context->GetAttr("number_colors", &number_colors));
+ OP_REQUIRES_OK(context,
+ context->GetAttr("output_image_shape", &output_image_shape));
+ OP_REQUIRES_OK(context,
+ context->GetAttr("output_data_window", &output_data_window));
+
+ E2Epixels =
+ eye_separation * dots_per_inch; // Initialize pixels from eye to eye
+ }
+
+ ~SingleImageRandomDotStereogramsOp() { // Destructor
+ }
+
+ void Compute(OpKernelContext* context) override {
+ const Tensor& input_tensor = context->input(0);
+ input_Xvalue = input_tensor.shape().dim_size(
+ 1); // X value is the number of columns of the input matrix
+ input_Yvalue =
+ input_tensor.shape().dim_size(0); // Y value is the number of rows
+
+ output_Ximage = output_image_shape.dim(0).size();
+ output_Yimage = output_image_shape.dim(1).size();
+ output_Cimage = output_image_shape.dim(2).size();
+
+ if (number_colors > 256) // Go to full color image
+ output_Cimage = 3;
+
+ int data_Xwindow = output_data_window.dim(0).size();
+ int data_Ywindow = output_data_window.dim(1).size();
+
+ int deltaX_border_image = output_Ximage - data_Xwindow;
+ int deltaY_border_image = output_Yimage - data_Ywindow;
+
+ if (convergence_dots_size >
+ 0) // 3 frame sections in Y direction due to DOTS
+ {
+ deltaY_border_image =
+ deltaY_border_image -
+ convergence_dots_size; // Take off space for Convergence Dots
+ deltaY_border_image = std::max(0, deltaY_border_image);
+ data_box_top = deltaY_border_image / 3;
+
+ if (deltaY_border_image >= 0) {
+ converge_dot_box_end = output_Yimage - 1 - data_box_top;
+ } else {
+ converge_dot_box_end = output_Yimage - 1;
+ }
+ } else // Otherwise only 2, no convergence dot
+ {
+ data_box_top = deltaY_border_image / 2; // Center DATA in Y dimension
+ converge_dot_box_end = output_Yimage - 1;
+ }
+
+ data_box_left = deltaX_border_image / 2; // Center DATA in X dimension
+ data_box_width = data_Xwindow; // width of scan line
+ data_box_height = data_Ywindow; // hight of image
+
+ const T* inputZ = input_tensor.flat<T>().data(); // Flatten input Z buffer
+
+ BuildZBuffer(inputZ);
+
+ // Output a scalar string.
+ Tensor* output_tensor = NULL;
+ OP_REQUIRES_OK(
+ context,
+ context->allocate_output(
+ 0, TensorShape({output_Yimage, output_Ximage, output_Cimage}),
+ &output_tensor));
+
+ outputImage = output_tensor->flat<uint8>().data();
+
+ generate_stereogram();
+
+ delete[] ZBuffer;
+ }
+
+ //***************************************************************************
+ //***************************************************************************
+ // Move input into standard Z format to reduce complexity of algorithm
+ //
+ void BuildZBuffer(const T* Z, bool log = false) {
+ double MaxValue = 1.0;
+ double MinValue = 0.0;
+ ZBuffer = new double[input_Xvalue * input_Yvalue]; // Used to computer
+ // final Z values before
+ // rendering to output
+
+ if (normalize) {
+ // Init Min/Max to first value
+ if (normalize_max < normalize_min) // Autoscale if MIN>MAX
+ {
+ MaxValue = (double)*Z;
+ MinValue = (double)*Z;
+
+ for (int y = 0; y < input_Yvalue; ++y)
+ for (int x = 0; x < input_Xvalue; ++x) {
+ double value = getZfromInputImage(Z, x, y);
+ if (value > MaxValue) MaxValue = value;
+ if (value < MinValue) MinValue = value;
+ }
+ } else {
+ MaxValue = normalize_max;
+ MinValue = normalize_min;
+ }
+ }
+
+ for (int y = 0; y < input_Yvalue; ++y)
+ for (int x = 0; x < input_Xvalue; ++x) {
+ double value = getZfromInputImage(Z, x, y);
+
+ if (normalize) {
+ value = (value - MinValue) / (MaxValue - MinValue);
+ }
+
+ if (value > 1.0) value = 1.0;
+ if (value < 0.0) value = 0.0;
+
+ *(ZBuffer + (input_Xvalue * y + x)) = value;
+ }
+ }
+
+ //***************************************************************************
+ //***************************************************************************
+ double getZfromInputImage(const T* Z, int x, int y) {
+ double return_val;
+
+ return_val = (double)*(Z + input_Xvalue * y + x); // Get value
+ return return_val;
+ }
+
+ //***************************************************************************
+ //***************************************************************************
+ // All normalized, not checking required
+ // Possible Projection issue if DATA is bigger or smaller than Input
+ // Modes include:
+ // Truncate value (Default)
+ // Round-off value
+ // Interpolate between values
+ //
+ double getZfromZbuffer(double x, double y) {
+ int xi, yi;
+
+ switch (indexMode) {
+ case 0: // Truncate
+ xi = int(x);
+ yi = int(y);
+ return (*(ZBuffer + (xi + input_Xvalue * yi)));
+ break;
+ case 1: // Round-off
+ xi = std::round(x);
+ yi = std::round(y);
+ return (*(ZBuffer + (xi + input_Xvalue * yi)));
+ break;
+ case 2: // Interpolate (Not implemented yet, will need 4 points
+ // [x,y],[x+1,y],[x,y+1],[x+1,y+1], then interpolate)
+ xi = int(x);
+ yi = int(y);
+ return (*(ZBuffer + (xi + input_Xvalue * yi)));
+ break;
+ default: // Round-off is the default
+ xi = int(x + 0.5);
+ yi = int(y + 0.5);
+ return (*(ZBuffer + (xi + input_Xvalue * yi)));
+ break;
+ }
+ }
+
+ //***************************************************************************
+ //***************************************************************************
+
+ int getOutputImageIndex(int x, int y,
+ int channel) { // No error checking for some
+ // optimization, calling routine
+ // required to make sure there is no
+ // violation
+ return ((output_Ximage * output_Cimage) * y + x * output_Cimage + channel);
+ }
+
+ //***************************************************************************
+ //***************************************************************************
+
+ double getZFromOutputPixel(int x, int y) {
+ double xofz, yofz, returnval;
+
+ // Convert pixel units to Z units, do this as "double"
+
+ xofz =
+ (double)input_Xvalue * (x - data_box_left) / ((double)data_box_width);
+ yofz =
+ (double)input_Yvalue * (y - data_box_top) / ((double)data_box_height);
+
+ if ((xofz < 0) || (yofz < 0) || (yofz >= input_Yvalue) ||
+ (xofz >= input_Xvalue)) { // Top of left side border hit or Right
+ // side or bottom border hit
+ // Send BORDER Z value
+ return (border_level);
+ }
+
+ { // in data set Z interpolate if need
+ double gz;
+
+ gz = getZfromZbuffer(xofz, yofz);
+
+ returnval = gz;
+ }
+
+ return (returnval);
+ }
+
+ //***************************************************************************
+ //***************************************************************************
+
+ void generate_stereogram() {
+ int s, left, right, visible, t, l;
+ double zt, gz;
+ // Scan line
+ uint8* pix; // Scan row color for each pixel
+ int* same; // Used to determine if Pixel needs to be the same as another
+ // pixel in the row
+
+ pix = new uint8[output_Ximage * output_Cimage];
+ same = new int[output_Ximage];
+
+ for (int y = 0; y < output_Yimage; ++y) {
+ // Set no dependencies on any pixels, tie each one back to itself
+ for (int x = 0; x < output_Ximage; ++x) same[x] = x;
+
+ for (int x = 0; x < output_Ximage; ++x) {
+ gz = getZFromOutputPixel(x, y);
+ s = separation(gz);
+ left = x - s / 2;
+ right = left + s;
+
+ if ((left >= 0) && (right < output_Ximage)) {
+ t = 1;
+ visible = 1;
+ if (hidden_surface_removal) do {
+ zt = gz + 2 * (2 - mu * gz) * t / (mu * E2Epixels);
+ visible = (getZFromOutputPixel(x - t, y) < zt) &&
+ (getZFromOutputPixel(x + t, y) < zt);
+ ++t;
+ } while ((visible) && (zt < 1));
+
+ if (visible) {
+ l = same[left];
+ while ((l != left) && (l != right))
+ if (l < right) {
+ left = l;
+ l = same[left];
+ } else {
+ same[left] = right;
+ left = right;
+ l = same[left];
+ right = l;
+ }
+ same[left] = right;
+ }
+ }
+ }
+ // Set colors for scan row, use channels and number_colors
+ for (int x = output_Ximage - 1; x >= 0; x--) {
+ for (int channel = 0; channel < output_Cimage; ++channel) {
+ if (same[x] == x) { // Pick a random color
+ if (number_colors == 2) {
+ if ((rand() % 2) == 0) {
+ pix[x * output_Cimage + channel] = Cblack;
+ } else {
+ pix[x * output_Cimage + channel] = Cwhite;
+ }
+ } else {
+ pix[x * output_Cimage + channel] = rand() % 256;
+ }
+ } else
+ pix[x * output_Cimage + channel] =
+ pix[same[x] * output_Cimage + channel];
+
+ setpixel(x, y, channel, pix[x * output_Cimage + channel]);
+ }
+ }
+ }
+
+ draw_convergence_dots();
+
+ delete[] pix;
+ delete[] same;
+ }
+
+ //***************************************************************************
+ //***************************************************************************
+
+ void draw_convergence_dots() {
+ int x1, x2; // center position for convergence dots
+
+ if (convergence_dots_size == 0) // No dot, return
+ return;
+
+ x1 = output_Ximage / 2 - get_far_width() / 2;
+ x2 = output_Ximage / 2 + get_far_width() / 2;
+
+ for (int lloop = 0; lloop < convergence_dots_size; ++lloop)
+ for (int wloop = 0; wloop < convergence_dots_size; ++wloop)
+ for (int channel = 0; channel < output_Cimage; ++channel) {
+ setpixel(x1 - (convergence_dots_size / 2) + wloop,
+ converge_dot_box_end - lloop, channel, Cblack);
+ setpixel(x2 - (convergence_dots_size / 2) + wloop,
+ converge_dot_box_end - lloop, channel, Cblack);
+ }
+ }
+
+ //***************************************************************************
+ //***************************************************************************
+
+ void setpixel(int x, int y, int channel, uint8 color) {
+ *(outputImage + getOutputImageIndex(x, y, channel)) = color;
+ }
+};
+
+#define REGISTER_KERNEL(T) \
+ REGISTER_KERNEL_BUILDER(Name("SingleImageRandomDotStereograms") \
+ .Device(DEVICE_CPU) \
+ .TypeConstraint<T>("T"), \
+ SingleImageRandomDotStereogramsOp<T>);
+
+REGISTER_KERNEL(int32);
+REGISTER_KERNEL(int64);
+REGISTER_KERNEL(float);
+REGISTER_KERNEL(double);
+
+#undef REGISTER_KERNEL
+
+} // end namespace tensorflow