Unverified Commit bd9c6c4e authored by André Araujo's avatar André Araujo Committed by GitHub
Browse files

Move DELF resizing code into utils.py, which is more appropriate than extractor.py. (#9108)

* Merged commit includes the following changes:
326723075  by Andre Araujo:

    Move image resize utility into utils.py.

--

PiperOrigin-RevId: 326723075

* Adding back matched_images_demo.png
parent e8ea6b7d
...@@ -19,81 +19,18 @@ from __future__ import division ...@@ -19,81 +19,18 @@ from __future__ import division
from __future__ import print_function from __future__ import print_function
import numpy as np import numpy as np
from PIL import Image
import tensorflow as tf import tensorflow as tf
from delf import datum_io from delf import datum_io
from delf import feature_extractor from delf import feature_extractor
from delf import utils
# Minimum dimensions below which DELF features are not extracted (empty # Minimum dimensions below which features are not extracted (empty
# features are returned). This applies after any resizing is performed. # features are returned). This applies after any resizing is performed.
_MIN_HEIGHT = 10 _MIN_HEIGHT = 10
_MIN_WIDTH = 10 _MIN_WIDTH = 10
def ResizeImage(image, config, resize_factor=1.0):
"""Resizes image according to config.
Args:
image: Uint8 array with shape (height, width, 3).
config: DelfConfig proto containing the model configuration.
resize_factor: Optional float resize factor for the input image. If given,
the maximum and minimum allowed image sizes in `config` are scaled by this
factor. Must be non-negative.
Returns:
resized_image: Uint8 array with resized image.
scale_factors: 2D float array, with factors used for resizing along height
and width (If upscaling, larger than 1; if downscaling, smaller than 1).
Raises:
ValueError: If `image` has incorrect number of dimensions/channels.
"""
if resize_factor < 0.0:
raise ValueError('negative resize_factor is not allowed: %f' %
resize_factor)
if image.ndim != 3:
raise ValueError('image has incorrect number of dimensions: %d' %
image.ndims)
height, width, channels = image.shape
# Take into account resize factor.
max_image_size = resize_factor * config.max_image_size
min_image_size = resize_factor * config.min_image_size
if channels != 3:
raise ValueError('image has incorrect number of channels: %d' % channels)
largest_side = max(width, height)
if max_image_size >= 0 and largest_side > max_image_size:
scale_factor = max_image_size / largest_side
elif min_image_size >= 0 and largest_side < min_image_size:
scale_factor = min_image_size / largest_side
elif config.use_square_images and (height != width):
scale_factor = 1.0
else:
# No resizing needed, early return.
return image, np.ones(2, dtype=float)
# Note that new_shape is in (width, height) format (PIL convention), while
# scale_factors are in (height, width) convention (NumPy convention).
if config.use_square_images:
new_shape = (int(round(largest_side * scale_factor)),
int(round(largest_side * scale_factor)))
else:
new_shape = (int(round(width * scale_factor)),
int(round(height * scale_factor)))
scale_factors = np.array([new_shape[1] / height, new_shape[0] / width],
dtype=float)
pil_image = Image.fromarray(image)
resized_image = np.array(pil_image.resize(new_shape, resample=Image.BILINEAR))
return resized_image, scale_factors
def MakeExtractor(config): def MakeExtractor(config):
"""Creates a function to extract global and/or local features from an image. """Creates a function to extract global and/or local features from an image.
...@@ -206,7 +143,7 @@ def MakeExtractor(config): ...@@ -206,7 +143,7 @@ def MakeExtractor(config):
features (key 'local_features' mapping to a dict with keys 'locations', features (key 'local_features' mapping to a dict with keys 'locations',
'descriptors', 'scales', 'attention'). 'descriptors', 'scales', 'attention').
""" """
resized_image, scale_factors = ResizeImage( resized_image, scale_factors = utils.ResizeImage(
image, config, resize_factor=resize_factor) image, config, resize_factor=resize_factor)
# If the image is too small, returns empty features. # If the image is too small, returns empty features.
......
...@@ -18,6 +18,7 @@ from __future__ import absolute_import ...@@ -18,6 +18,7 @@ from __future__ import absolute_import
from __future__ import division from __future__ import division
from __future__ import print_function from __future__ import print_function
import numpy as np
from PIL import Image from PIL import Image
from PIL import ImageFile from PIL import ImageFile
import tensorflow as tf import tensorflow as tf
...@@ -39,3 +40,65 @@ def RgbLoader(path): ...@@ -39,3 +40,65 @@ def RgbLoader(path):
img = Image.open(f) img = Image.open(f)
return img.convert('RGB') return img.convert('RGB')
def ResizeImage(image, config, resize_factor=1.0):
"""Resizes image according to config.
Args:
image: Uint8 array with shape (height, width, 3).
config: DelfConfig proto containing the model configuration.
resize_factor: Optional float resize factor for the input image. If given,
the maximum and minimum allowed image sizes in `config` are scaled by this
factor. Must be non-negative.
Returns:
resized_image: Uint8 array with resized image.
scale_factors: 2D float array, with factors used for resizing along height
and width (If upscaling, larger than 1; if downscaling, smaller than 1).
Raises:
ValueError: If `image` has incorrect number of dimensions/channels.
"""
if resize_factor < 0.0:
raise ValueError('negative resize_factor is not allowed: %f' %
resize_factor)
if image.ndim != 3:
raise ValueError('image has incorrect number of dimensions: %d' %
image.ndims)
height, width, channels = image.shape
# Take into account resize factor.
max_image_size = resize_factor * config.max_image_size
min_image_size = resize_factor * config.min_image_size
if channels != 3:
raise ValueError('image has incorrect number of channels: %d' % channels)
largest_side = max(width, height)
if max_image_size >= 0 and largest_side > max_image_size:
scale_factor = max_image_size / largest_side
elif min_image_size >= 0 and largest_side < min_image_size:
scale_factor = min_image_size / largest_side
elif config.use_square_images and (height != width):
scale_factor = 1.0
else:
# No resizing needed, early return.
return image, np.ones(2, dtype=float)
# Note that new_shape is in (width, height) format (PIL convention), while
# scale_factors are in (height, width) convention (NumPy convention).
if config.use_square_images:
new_shape = (int(round(largest_side * scale_factor)),
int(round(largest_side * scale_factor)))
else:
new_shape = (int(round(width * scale_factor)),
int(round(height * scale_factor)))
scale_factors = np.array([new_shape[1] / height, new_shape[0] / width],
dtype=float)
pil_image = Image.fromarray(image)
resized_image = np.array(pil_image.resize(new_shape, resample=Image.BILINEAR))
return resized_image, scale_factors
# Copyright 2019 The TensorFlow Authors All Rights Reserved. # Copyright 2020 The TensorFlow Authors All Rights Reserved.
# #
# Licensed under the Apache License, Version 2.0 (the "License"); # Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License. # you may not use this file except in compliance with the License.
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# ============================================================================== # ==============================================================================
"""Tests for DELF feature extractor.""" """Tests for helper utilities."""
from __future__ import absolute_import from __future__ import absolute_import
from __future__ import division from __future__ import division
...@@ -23,10 +23,10 @@ import numpy as np ...@@ -23,10 +23,10 @@ import numpy as np
import tensorflow as tf import tensorflow as tf
from delf import delf_config_pb2 from delf import delf_config_pb2
from delf import extractor from delf import utils
class ExtractorTest(tf.test.TestCase, parameterized.TestCase): class UtilsTest(tf.test.TestCase, parameterized.TestCase):
@parameterized.named_parameters( @parameterized.named_parameters(
('Max-1Min-1', -1, -1, 1.0, False, [4, 2, 3], [1.0, 1.0]), ('Max-1Min-1', -1, -1, 1.0, False, [4, 2, 3], [1.0, 1.0]),
...@@ -70,8 +70,8 @@ class ExtractorTest(tf.test.TestCase, parameterized.TestCase): ...@@ -70,8 +70,8 @@ class ExtractorTest(tf.test.TestCase, parameterized.TestCase):
min_image_size=min_image_size, min_image_size=min_image_size,
use_square_images=square_output) use_square_images=square_output)
resized_image, scale_factors = extractor.ResizeImage( resized_image, scale_factors = utils.ResizeImage(image, config,
image, config, resize_factor) resize_factor)
self.assertAllEqual(resized_image.shape, expected_shape) self.assertAllEqual(resized_image.shape, expected_shape)
self.assertAllClose(scale_factors, expected_scale_factors) self.assertAllClose(scale_factors, expected_scale_factors)
...@@ -93,8 +93,8 @@ class ExtractorTest(tf.test.TestCase, parameterized.TestCase): ...@@ -93,8 +93,8 @@ class ExtractorTest(tf.test.TestCase, parameterized.TestCase):
min_image_size=min_image_size, min_image_size=min_image_size,
use_square_images=square_output) use_square_images=square_output)
resized_image, scale_factors = extractor.ResizeImage( resized_image, scale_factors = utils.ResizeImage(image, config,
image, config, resize_factor) resize_factor)
self.assertAllEqual(resized_image.shape, expected_shape) self.assertAllEqual(resized_image.shape, expected_shape)
self.assertAllClose(scale_factors, expected_scale_factors) self.assertAllClose(scale_factors, expected_scale_factors)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment