From 884b9687ae480fb665f2e08a465238f5e87bd58e Mon Sep 17 00:00:00 2001 From: "Mascha, Philipp" Date: Tue, 12 Nov 2019 10:51:48 +0100 Subject: [PATCH] Added the crop and resize method from the c++ library. --- .../Operations/image_ops_impl.cs | 49 +++++++++++------ .../img_test/TestCrop.cs | 53 +++++++++++++++++++ 2 files changed, 86 insertions(+), 16 deletions(-) create mode 100644 test/TensorFlowNET.UnitTest/img_test/TestCrop.cs diff --git a/src/TensorFlowNET.Core/Operations/image_ops_impl.cs b/src/TensorFlowNET.Core/Operations/image_ops_impl.cs index b7573b92..fd252790 100644 --- a/src/TensorFlowNET.Core/Operations/image_ops_impl.cs +++ b/src/TensorFlowNET.Core/Operations/image_ops_impl.cs @@ -14,9 +14,11 @@ limitations under the License. ******************************************************************************/ +using NumSharp; using System; using System.Collections.Generic; using System.Text; +using Tensorflow.Operations; using static Tensorflow.Binding; namespace Tensorflow @@ -102,6 +104,37 @@ namespace Tensorflow }); } + internal static Tensor resize_images(Tensor images, Tensor size, ResizeMethod method, bool align_corners, bool preserve_aspect_ratio, string name) + { + throw new NotImplementedException(); + } + + /// + /// Extracts crops from the input image tensor and resizes them using bilinear sampling or nearest neighbor sampling (possibly with aspect ratio change) to a common output size specified by crop_size. + /// This is more general than the crop_to_bounding_box op which extracts a fixed size slice from the input image and does not allow resizing or aspect ratio change. + /// Returns a tensor with crops from the input image at positions defined at the bounding box locations in boxes. + /// The cropped boxes are all resized(with bilinear or nearest neighbor interpolation) to a fixed size = [crop_height, crop_width]. + /// The result is a 4 - D tensor[num_boxes, crop_height, crop_width, depth]. + /// The resizing is corner aligned. In particular, if boxes = [[0, 0, 1, 1]], the method will give identical results to using tf.image.resize_bilinear() or tf.image.resize_nearest_neighbor() (depends on the method argument) with align_corners = True. + /// + /// A 4-D tensor of shape [batch, image_height, image_width, depth]. Both image_height and image_width need to be positive. + /// A 2-D tensor of shape [num_boxes, 4]. The i-th row of the tensor specifies the coordinates of a box in the box_ind[i] image and is specified in normalized coordinates [y1, x1, y2, x2]. A normalized coordinate value of y is mapped to the image coordinate at y * (image_height - 1), so as the [0, 1] interval of normalized image height is mapped to [0, image_height - 1] in image height coordinates. We do allow y1 > y2, in which case the sampled crop is an up-down flipped version of the original image. The width dimension is treated similarly. Normalized coordinates outside the [0, 1] range are allowed, in which case we use extrapolation_value to extrapolate the input image values. + /// A 1-D tensor of shape [num_boxes] with int32 values in [0, batch). The value of box_ind[i] specifies the image that the i-th box refers to. + /// A 1-D tensor of 2 elements, size = [crop_height, crop_width]. All cropped image patches are resized to this size. The aspect ratio of the image content is not preserved. Both crop_height and crop_width need to be positive. + /// A 4-D tensor of shape [num_boxes, crop_height, crop_width, depth]. + public static Tensor CropAndResize(Tensor image, Tensor boxes, Tensor box_ind, Tensor crop_size) + { + var _op = gen_nn_ops._op_def_lib._apply_op_helper("CropAndResize", name: null, args: new + { + image, + boxes, + box_ind, + crop_size + }); + + return _op.outputs[0]; + } + public static Tensor is_jpeg(Tensor contents, string name = null) { return tf_with(ops.name_scope(name, "is_jpeg"), scope => @@ -129,22 +162,6 @@ namespace Tensorflow throw new NotImplementedException(""); } - /// - /// Resize `images` to `size` using the specified `method`. - /// - /// - /// - /// - /// - /// - /// - /// - public static Tensor resize_images(Tensor images, Tensor size, ResizeMethod method = ResizeMethod.BILINEAR, - bool align_corners = false, bool preserve_aspect_ratio = false, string name = null) - { - throw new NotImplementedException(""); - } - /// /// Resize `images` to `size` using nearest neighbor interpolation. /// diff --git a/test/TensorFlowNET.UnitTest/img_test/TestCrop.cs b/test/TensorFlowNET.UnitTest/img_test/TestCrop.cs new file mode 100644 index 00000000..51ab2cd3 --- /dev/null +++ b/test/TensorFlowNET.UnitTest/img_test/TestCrop.cs @@ -0,0 +1,53 @@ +using FluentAssertions; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NumSharp; +using Tensorflow; +using static Tensorflow.Binding; + +namespace TensorFlowNET.UnitTest.img_test +{ + [TestClass] + public class TestCrop + { + + [TestMethod] + public void TestCropAndResize() + { + // 3x3 'Image' with numbered coordinates + var input = np.array(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f); + var image = tf.reshape(input, new int[] { 1, 3, 3, 1 }); + + // 4x4 'Image' with numbered coordinates + var input2 = np.array(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f, 9f, 10f, 11f, 12f, 13f, 14f, 15f); + var image2 = tf.reshape(input2, new int[] { 1, 4, 4, 1 }); + // create one box over the full image that flips it (y1 > y2) + var box = tf.reshape(np.array(1f, 0f, 0f, 1f), new int[] {1, 4}); + var boxInd = tf.Variable(np.array(0)); + // crop first 3x3 imageto size 1x1 + var cropSize1_1 = tf.Variable(np.array(1, 1)); + // don't crop second 4x4 image + var cropSize2_2 = tf.Variable(np.array(4, 4)); + + var init = tf.global_variables_initializer(); + using (Session sess = tf.Session()) + { + sess.run(init); + + var cropped = image_ops_impl.CropAndResize(image, box, boxInd, cropSize1_1); + + var result = sess.run(cropped); + // check if cropped to 1x1 center was succesfull + result.size.Should().Be(1); + result[0, 0, 0, 0].Should().Be(4f); + + cropped = image_ops_impl.CropAndResize(image2, box, boxInd, cropSize2_2); + result = sess.run(cropped); + // check if flipped and no cropping occured + result.size.Should().Be(16); + result[0, 0, 0, 0].Should().Be(12f); + } + + } + + } +}