1c8b59c046895fa5b6d79f73e0b5817330fcfbfc1A. Unique TensorFlower/* Copyright 2015 The TensorFlow Authors. All Rights Reserved. 2f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 3f3a77378b4c056e76691c5eba350a022c11e00d4Martin WickeLicensed under the Apache License, Version 2.0 (the "License"); 4f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wickeyou may not use this file except in compliance with the License. 5f3a77378b4c056e76691c5eba350a022c11e00d4Martin WickeYou may obtain a copy of the License at 6f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 7f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke http://www.apache.org/licenses/LICENSE-2.0 8f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 9f3a77378b4c056e76691c5eba350a022c11e00d4Martin WickeUnless required by applicable law or agreed to in writing, software 10f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wickedistributed under the License is distributed on an "AS IS" BASIS, 11f3a77378b4c056e76691c5eba350a022c11e00d4Martin WickeWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12f3a77378b4c056e76691c5eba350a022c11e00d4Martin WickeSee the License for the specific language governing permissions and 13f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wickelimitations under the License. 14f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke==============================================================================*/ 15f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// See docs in ../ops/image_ops.cc. 16fc53648e0c0f5110bfab75a02b9dc75260b913d3A. Unique TensorFlower#include <math.h> 17f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke#include "tensorflow/core/framework/op_kernel.h" 18f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke#include "tensorflow/core/framework/register_types.h" 19f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke#include "tensorflow/core/framework/tensor.h" 20f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke#include "tensorflow/core/framework/types.h" 21227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen#include "tensorflow/core/kernels/bounds_check.h" 22f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke#include "tensorflow/core/lib/random/simple_philox.h" 23f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke#include "tensorflow/core/util/guarded_philox_random.h" 24f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 25f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wickeusing tensorflow::random::SimplePhilox; 26f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 275cae4a52edf9fe4951c51ee2352ca041e4ab9363Derek Murraynamespace tensorflow { 28f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wickenamespace { 29f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 30f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// A simple Rectangle class that supplies intersection. 31f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wickeclass Rectangle { 32f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke public: 33f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke Rectangle() { Set(0, 0, 0, 0); } 34f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke Rectangle(int xmin, int ymin, int xmax, int ymax) { 35f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke Set(xmin, ymin, xmax, ymax); 36f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 37f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 38f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke void Set(int xmin, int ymin, int xmax, int ymax) { 39f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke min_x_ = xmin; 40f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke min_y_ = ymin; 41f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke max_x_ = xmax; 42f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke max_y_ = ymax; 43f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 44f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 45f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bool IsEmpty() const { return min_x_ > max_x_ || min_y_ > max_y_; } 46f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke float Area() const { 47f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke return static_cast<float>((max_x_ - min_x_) * (max_y_ - min_y_)); 48f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 49f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 50f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke Rectangle Intersect(const Rectangle& r) const { 51f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const int pmin_x = std::max(min_x_, r.min_x_); 52f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const int pmin_y = std::max(min_y_, r.min_y_); 53f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const int pmax_x = std::min(max_x_, r.max_x_); 54f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const int pmax_y = std::min(max_y_, r.max_y_); 55f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 56f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (pmin_x > pmax_x || pmin_y > pmax_y) { 57f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke return Rectangle(); 58f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } else { 59f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke return Rectangle(pmin_x, pmin_y, pmax_x, pmax_y); 60f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 61f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 62f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 63f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke int min_x_; 64f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke int min_y_; 65f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke int max_x_; 66f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke int max_y_; 67f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke}; 68f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 69f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// Determine if the supplied cropping box covers a sufficient fraction of the 70f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// the supplied bounding boxes. 71f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wickebool SatisfiesOverlapConstraints(const Rectangle& crop, 72f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke float minimum_object_covered, 73f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const std::vector<Rectangle>& bounding_boxes) { 74f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Reject any bounding box which contains no pixels. 75f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float kMinArea = 1.0; 76f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (crop.Area() < kMinArea) { 77f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke return false; 78f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 79f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 80f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Loop through all objects and determine if the proposed cropping box covers 81f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // a sufficient fraction of one of the supplied bounding boxes. 82f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bool is_object_covered = false; 83f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke for (const auto& bbox : bounding_boxes) { 84f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float object_area = bbox.Area(); 85f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (object_area < kMinArea) { 86f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke continue; 87f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 88f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 89f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float object_covered = crop.Intersect(bbox).Area() / object_area; 90f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 91f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (object_covered >= minimum_object_covered) { 92f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke is_object_covered = true; 93f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke break; 94f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 95f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 96f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke return is_object_covered; 97f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke} 98f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 99f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// Generate a random crop within the rectangle 100f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// (0, 0, original_width, original_height). 101f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// The minimum area of the crop will be between 102f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// min_relative_crop_area * orig_width * orig_height 103f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// and 104f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// max_relative_crop_area * orig_width * orig_height 105f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// such that its width = round(aspect_ratio * height). 106f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// The diameter of the generated rectangle will be uniformly distributed between 107f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// its minimum and maximum size. The center of the rectangle will be distributed 108f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// uniformly within the source rectangle. The function returns false if the 109f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke// rectangle could not be generated with the given constraints. 110f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wickebool GenerateRandomCrop(int original_width, int original_height, 111f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke float min_relative_crop_area, 112f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke float max_relative_crop_area, float aspect_ratio, 113f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke SimplePhilox* random, Rectangle* crop_rect) { 114f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (max_relative_crop_area <= 0.0 || aspect_ratio <= 0.0 || 115f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke original_width <= 0 || original_height <= 0 || 116f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke min_relative_crop_area > max_relative_crop_area) { 117f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke return false; 118f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 119f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 120f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float min_area = 121f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke min_relative_crop_area * original_width * original_height; 122f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float max_area = 123f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke max_relative_crop_area * original_width * original_height; 124f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 125227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen int height = static_cast<int>(lrintf(sqrt(min_area / aspect_ratio))); 126227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen int max_height = static_cast<int>(lrintf(sqrt(max_area / aspect_ratio))); 127f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 128fc53648e0c0f5110bfab75a02b9dc75260b913d3A. Unique TensorFlower if (lrintf(max_height * aspect_ratio) > original_width) { 129f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // We must find the smallest max_height satisfying 130f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // round(max_height * aspect_ratio) <= original_width: 131f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float kEps = 0.0000001; 132fc53648e0c0f5110bfab75a02b9dc75260b913d3A. Unique TensorFlower max_height = static_cast<int>((original_width + 0.5 - kEps) / aspect_ratio); 133f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 134f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 135f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (max_height > original_height) { 136f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke max_height = original_height; 137f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 138f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 139f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (height >= max_height) { 140f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke height = max_height; 141f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 142f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 143f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (height < max_height) { 144f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // We need to generate a random number in the closed range 145f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // [0, max_height - height]. 146f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke height += random->Uniform(max_height - height + 1); 147f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 148227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen int width = static_cast<int>(lrintf(height * aspect_ratio)); 149f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke DCHECK_LE(width, original_width); 150f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 151f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Let us not fail if rounding error causes the area to be 152f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // outside the constraints. 153f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Try first with a slightly bigger rectangle first. 154f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke float area = static_cast<float>(width * height); 155f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (area < min_area) { 156f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke height += 1; 157227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen width = static_cast<int>(lrintf(height * aspect_ratio)); 158f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke area = width * height; 159f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 160f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 161f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Let us not fail if rounding error causes the area to be 162f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // outside the constraints. 163f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Try first with a slightly smaller rectangle first. 164f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (area > max_area) { 165f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke height -= 1; 166227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen width = static_cast<int>(lrintf(height * aspect_ratio)); 167f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke area = width * height; 168f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 169f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 170f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Now, we explored all options to rectify small rounding errors. 171f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // It seems the constraints can't be satisfied: return false. 172f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (area < min_area || area > max_area || width > original_width || 173f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke height > original_height || width <= 0 || height <= 0) { 174f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke return false; 175f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 176f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 177f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke int y = 0; 178f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (height < original_height) { 179f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke y = random->Uniform(original_height - height); 180f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 181f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke int x = 0; 182f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (width < original_width) { 183f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke x = random->Uniform(original_width - width); 184f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 185f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 186f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke crop_rect->min_x_ = x; 187f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke crop_rect->min_y_ = y; 188f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke crop_rect->max_x_ = x + width; 189f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke crop_rect->max_y_ = y + height; 190f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke return true; 191f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke} 192f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke} // namespace 193f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 194f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicketemplate <typename T> 195481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tangclass SampleDistortedBoundingBoxV2Op : public OpKernel { 196f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke public: 197481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang explicit SampleDistortedBoundingBoxV2Op(OpKernelConstruction* context) 198f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke : OpKernel(context) { 199f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES_OK(context, generator_.Init(context)); 200f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 201481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang if (context->num_inputs() == 2) { 202481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang OP_REQUIRES_OK(context, context->GetAttr("min_object_covered", 203481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang &min_object_covered_)); 204481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang OP_REQUIRES( 205481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang context, min_object_covered_ >= 0, 206481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang errors::InvalidArgument("Min object covered must be non-negative: ", 207481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang min_object_covered_)); 208481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang } 209f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 210f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES_OK(context, context->GetAttr("use_image_if_no_bounding_boxes", 211f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke &use_image_if_no_bounding_boxes_)); 212f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 213f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES_OK( 214f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke context, context->GetAttr("aspect_ratio_range", &aspect_ratio_range_)); 215f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, aspect_ratio_range_.size() == 2, 216f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument( 217f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "Aspect ratio range field must specify 2 dimensions")); 218f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 219f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES( 220f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke context, aspect_ratio_range_[0] > 0 && aspect_ratio_range_[1] > 0, 221f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument("Aspect ratio range must be non-negative: [", 222f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke aspect_ratio_range_[0], ", ", 223f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke aspect_ratio_range_[1], "]")); 224f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 225f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES_OK(context, context->GetAttr("area_range", &area_range_)); 226f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES( 227f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke context, area_range_.size() == 2, 228f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument("Area range field must specify 2 dimensions")); 229f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 230f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES( 231f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke context, area_range_[0] > 0 && area_range_[1] > 0, 232f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument("Area range must be non-negative: [", 233f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke area_range_[0], ", ", area_range_[1], "]")); 234f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 235f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, area_range_[0] <= 1 && area_range_[1] <= 1, 236f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument( 237f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "Area range must be less then or equal to 1.0: [", 238f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke area_range_[0], ", ", area_range_[1], "]")); 239f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 240f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES_OK(context, context->GetAttr("max_attempts", &max_attempts_)); 241f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, max_attempts_ > 0, 242f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument("Max attempts must be non-negative: ", 243f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke max_attempts_)); 244f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 245f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 246f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke void Compute(OpKernelContext* context) override { 247f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const Tensor& image_size = context->input(0); 248f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 249f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, image_size.dims() == 1, 250f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument("image_size must be 1-dimensional", 251f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke image_size.shape().DebugString())); 252f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, image_size.dim_size(0) == 3, 253227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen errors::InvalidArgument("image_size must contain 3 elements", 254f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke image_size.shape().DebugString())); 255f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 256f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Note image_size_data(2) is the depth and unused. 257227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen const uint64 height_raw = internal::SubtleMustCopy(image_size.flat<T>()(0)); 258227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen const uint64 width_raw = internal::SubtleMustCopy(image_size.flat<T>()(1)); 259227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen OP_REQUIRES(context, 260227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen FastBoundsCheck(height_raw, std::numeric_limits<int32>::max()), 261227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen errors::InvalidArgument("image height cannot be >= int32 max")); 262227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen OP_REQUIRES(context, 263227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen FastBoundsCheck(width_raw, std::numeric_limits<int32>::max()), 264227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen errors::InvalidArgument("image width cannot be >= int32 max")); 265227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen const int32 height = static_cast<int32>(height_raw); 266227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. Andersen const int32 width = static_cast<int32>(width_raw); 267f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 268f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Ensure that the supplied bounding boxes are sane and convert them to 269f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Rectangles. 270f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const Tensor& input_boxes = context->input(1); 271f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, input_boxes.dims() == 3, 272f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument("input boxes must be 3-dimensional " 273f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "[batch, num_boxes, coords]: ", 274f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke input_boxes.shape().DebugString())); 275f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, input_boxes.dim_size(input_boxes.dims() - 1) == 4, 276f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument( 277f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "bounding boxes must have shape [4] or [*, 4], got ", 278f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke input_boxes.shape().DebugString())); 279f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 280481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang float min_object_covered_val = 0.0; 281481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang if (context->num_inputs() == 3) { 282481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang const Tensor& min_object_covered = context->input(2); 283481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang 284481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang OP_REQUIRES( 285481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang context, TensorShapeUtils::IsScalar(min_object_covered.shape()), 286481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang errors::InvalidArgument("min_object_covered must be 0-D, got shape ", 287481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang min_object_covered.shape().DebugString())); 288481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang 289481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang min_object_covered_val = min_object_covered.scalar<float>()(); 290481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang 291481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang OP_REQUIRES( 292481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang context, min_object_covered_val >= 0, 293481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang errors::InvalidArgument("Min object covered must be non-negative: ", 294481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang min_object_covered_val)); 295481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang } else { 296481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang min_object_covered_val = min_object_covered_; 297481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang } 298481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang 299f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke std::vector<Rectangle> bounding_boxes; 300f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (input_boxes.NumElements() > 0) { 301f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke TTypes<float>::ConstMatrix boxes = input_boxes.flat_inner_dims<float>(); 302f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke for (int b = 0; b < boxes.dimension(0); ++b) { 303f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke for (int i = 0; i < 4; ++i) { 304f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES( 305f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke context, boxes(b, i) >= 0.0 && boxes(b, i) <= 1.0, 306f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument("All bounding box coordinates must " 307f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "be in [0.0, 1.0]: ", 308f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke boxes(b, i))); 309f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 310f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 311fc53648e0c0f5110bfab75a02b9dc75260b913d3A. Unique TensorFlower const int32 x_min = static_cast<int32>(boxes(b, 1) * width); 312fc53648e0c0f5110bfab75a02b9dc75260b913d3A. Unique TensorFlower const int32 y_min = static_cast<int32>(boxes(b, 0) * height); 313fc53648e0c0f5110bfab75a02b9dc75260b913d3A. Unique TensorFlower const int32 x_max = static_cast<int32>(boxes(b, 3) * width); 314fc53648e0c0f5110bfab75a02b9dc75260b913d3A. Unique TensorFlower const int32 y_max = static_cast<int32>(boxes(b, 2) * height); 315f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 316f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bounding_boxes.push_back(Rectangle(x_min, y_min, x_max, y_max)); 317f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 318f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 319f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 320f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Insert the entire image if no bounding boxes are supplied. 321f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const Rectangle image_rect(0, 0, width, height); 3227280dafca161eb3413ea120d3dd07c63e5254e72A. Unique TensorFlower if (bounding_boxes.empty()) { 323f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, use_image_if_no_bounding_boxes_, 324f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::InvalidArgument( 325f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "No bounding boxes provided as input. One must " 326f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "enable use_image_if_no_bounding_boxes if you wish " 327f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "to not provide any bounding boxes.")); 328f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bounding_boxes.push_back(image_rect); 329f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 330f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 331f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float min_sample_area = area_range_[0]; 332f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float max_sample_area = area_range_[1]; 333f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float min_sample_aspect_ratio = aspect_ratio_range_[0]; 334f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float max_sample_aspect_ratio = aspect_ratio_range_[1]; 335f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 336f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke auto local_gen = generator_.ReserveSamples32(4 * max_attempts_); 337f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke random::SimplePhilox random(&local_gen); 338f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 339f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke Rectangle crop_rect; 340f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bool sample_generated = false; 341f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke for (int i = 0; i < max_attempts_; ++i) { 342f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const float sample_aspect_ratio = 343f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke random.RandFloat() * 344f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke (max_sample_aspect_ratio - min_sample_aspect_ratio) + 345f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke min_sample_aspect_ratio; 346f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 347f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (GenerateRandomCrop(width, height, min_sample_area, max_sample_area, 348f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke sample_aspect_ratio, &random, &crop_rect)) { 349481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang if (SatisfiesOverlapConstraints(crop_rect, min_object_covered_val, 350f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bounding_boxes)) { 351f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke sample_generated = true; 352f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke break; 353f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 354f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 355f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 356f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 357f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke if (!sample_generated) { 358f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke crop_rect = image_rect; 359f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 360f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 361f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Determine the cropping parameters from the bounding box. 362f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const int target_width = crop_rect.max_x_ - crop_rect.min_x_; 363f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const int target_height = crop_rect.max_y_ - crop_rect.min_y_; 364f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 365f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const int offset_width = crop_rect.min_x_; 366f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke const int offset_height = crop_rect.min_y_; 367f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 368f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Ensure that the bounding box fits in the image dimensions. 369f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, width >= target_width + offset_width, 370f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::FailedPrecondition( 371f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "width must be > target_width + offset_width: ", width, 372f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "vs ", target_width, " + ", offset_width)); 373f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES(context, height >= target_height + offset_height, 374f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke errors::FailedPrecondition( 375f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke "height must be >= target_height: height = ", height, "vs ", 376f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke target_height, " + ", offset_height)); 377f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 378f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Create two vectors, each 3 elements, to provide as arguments to Slice. 379f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // See Slice() operation for details. 380f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke Tensor* begin = nullptr; 381f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES_OK(context, 382f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke context->allocate_output(0, TensorShape({3}), &begin)); 383f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke Tensor* size = nullptr; 384f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES_OK(context, 385f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke context->allocate_output(1, TensorShape({3}), &size)); 386f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke Tensor* bboxes = nullptr; 387f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke OP_REQUIRES_OK( 388f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke context, context->allocate_output(2, TensorShape({1, 1, 4}), &bboxes)); 389f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 3909832df4e7acf093e2fccbb84c5362ee201ea4321fo typename TTypes<T, 1>::Tensor begin_data(begin->tensor<T, 1>()); 3919832df4e7acf093e2fccbb84c5362ee201ea4321fo typename TTypes<T, 1>::Tensor size_data(size->tensor<T, 1>()); 3929832df4e7acf093e2fccbb84c5362ee201ea4321fo TTypes<float, 3>::Tensor bboxes_data = bboxes->tensor<float, 3>(); 393f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 394fe60adfb9d46f662f6ee216db23409efdbbb20e2A. Unique TensorFlower begin_data(0) = T(offset_height); 395fe60adfb9d46f662f6ee216db23409efdbbb20e2A. Unique TensorFlower size_data(0) = T(target_height); 396f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 397fe60adfb9d46f662f6ee216db23409efdbbb20e2A. Unique TensorFlower begin_data(1) = T(offset_width); 398fe60adfb9d46f662f6ee216db23409efdbbb20e2A. Unique TensorFlower size_data(1) = T(target_width); 399f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 400f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bboxes_data(0, 0, 0) = 401f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke static_cast<float>(crop_rect.min_y_) / static_cast<float>(height); 402f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bboxes_data(0, 0, 1) = 403f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke static_cast<float>(crop_rect.min_x_) / static_cast<float>(width); 404f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bboxes_data(0, 0, 2) = 405f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke static_cast<float>(crop_rect.max_y_) / static_cast<float>(height); 406f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bboxes_data(0, 0, 3) = 407f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke static_cast<float>(crop_rect.max_x_) / static_cast<float>(width); 408f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 409f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke // Retain all of the channels. 410fe60adfb9d46f662f6ee216db23409efdbbb20e2A. Unique TensorFlower begin_data(2) = T(0); 411fe60adfb9d46f662f6ee216db23409efdbbb20e2A. Unique TensorFlower size_data(2) = T(-1); 412f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke } 413f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 414f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke private: 415f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke GuardedPhiloxRandom generator_; 416f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke int32 max_attempts_; 417f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke std::vector<float> area_range_; 418f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke std::vector<float> aspect_ratio_range_; 419f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke float min_object_covered_; 420f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke bool use_image_if_no_bounding_boxes_; 421f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke}; 422f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 423481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang#define REGISTER_KERNELS(type) \ 424481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang REGISTER_KERNEL_BUILDER(Name("SampleDistortedBoundingBox") \ 425481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang .Device(DEVICE_CPU) \ 426481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang .TypeConstraint<type>("T"), \ 427481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang SampleDistortedBoundingBoxV2Op<type>) \ 428481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang REGISTER_KERNEL_BUILDER(Name("SampleDistortedBoundingBoxV2") \ 429481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang .Device(DEVICE_CPU) \ 430481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang .TypeConstraint<type>("T"), \ 431481a4b4c8c773196943aa04cb19a3ccbeb8c8c38Yong Tang SampleDistortedBoundingBoxV2Op<type>) 432f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 433227e7db4b0d3d9ff8325b1f0f3a2ee1f91c8eb0cDavid G. AndersenTF_CALL_INTEGRAL_TYPES(REGISTER_KERNELS); 434f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke#undef REGISTER_KERNELS 435f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke 436f3a77378b4c056e76691c5eba350a022c11e00d4Martin Wicke} // namespace tensorflow 437