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"""Python layer for distort_image_ops."""
16from __future__ import absolute_import
17from __future__ import division
18from __future__ import print_function
19
20from tensorflow.contrib.image.ops import gen_distort_image_ops
21from tensorflow.contrib.util import loader
22from tensorflow.python.framework import dtypes
23from tensorflow.python.framework import ops
24from tensorflow.python.ops import image_ops
25from tensorflow.python.ops import random_ops
26from tensorflow.python.platform import resource_loader
27
28_distort_image_ops = loader.load_op_library(
29    resource_loader.get_path_to_datafile('_distort_image_ops.so'))
30
31
32# pylint: disable=invalid-name
33def random_hsv_in_yiq(image,
34                      max_delta_hue=0,
35                      lower_saturation=1,
36                      upper_saturation=1,
37                      lower_value=1,
38                      upper_value=1,
39                      seed=None):
40  """Adjust hue, saturation, value of an RGB image randomly in YIQ color space.
41
42  Equivalent to `adjust_yiq_hsv()` but uses a `delta_h` randomly
43  picked in the interval `[-max_delta_hue, max_delta_hue]`, a `scale_saturation`
44  randomly picked in the interval `[lower_saturation, upper_saturation]`, and
45  a `scale_value` randomly picked in the interval
46  `[lower_saturation, upper_saturation]`.
47
48  Args:
49    image: RGB image or images. Size of the last dimension must be 3.
50    max_delta_hue: float. Maximum value for the random delta_hue. Passing 0
51                   disables adjusting hue.
52    lower_saturation: float.  Lower bound for the random scale_saturation.
53    upper_saturation: float.  Upper bound for the random scale_saturation.
54    lower_value: float.  Lower bound for the random scale_value.
55    upper_value: float.  Upper bound for the random scale_value.
56    seed: An operation-specific seed. It will be used in conjunction
57      with the graph-level seed to determine the real seeds that will be
58      used in this operation. Please see the documentation of
59      set_random_seed for its interaction with the graph-level random seed.
60
61  Returns:
62    3-D float tensor of shape `[height, width, channels]`.
63
64  Raises:
65    ValueError: if `max_delta`, `lower_saturation`, `upper_saturation`,
66               `lower_value`, or `upper_Value` is invalid.
67  """
68  if max_delta_hue < 0:
69    raise ValueError('max_delta must be non-negative.')
70
71  if lower_saturation < 0:
72    raise ValueError('lower_saturation must be non-negative.')
73
74  if lower_value < 0:
75    raise ValueError('lower_value must be non-negative.')
76
77  if lower_saturation > upper_saturation:
78    raise ValueError('lower_saturation must be < upper_saturation.')
79
80  if lower_value > upper_value:
81    raise ValueError('lower_value must be < upper_value.')
82
83  if max_delta_hue == 0:
84    delta_hue = 0
85  else:
86    delta_hue = random_ops.random_uniform(
87        [], -max_delta_hue, max_delta_hue, seed=seed)
88  if lower_saturation == upper_saturation:
89    scale_saturation = lower_saturation
90  else:
91    scale_saturation = random_ops.random_uniform(
92        [], lower_saturation, upper_saturation, seed=seed)
93  if lower_value == upper_value:
94    scale_value = lower_value
95  else:
96    scale_value = random_ops.random_uniform(
97        [], lower_value, upper_value, seed=seed)
98  return adjust_hsv_in_yiq(image, delta_hue, scale_saturation, scale_value)
99
100
101def adjust_hsv_in_yiq(image,
102                      delta_hue=0,
103                      scale_saturation=1,
104                      scale_value=1,
105                      name=None):
106  """Adjust hue, saturation, value of an RGB image in YIQ color space.
107
108  This is a convenience method that converts an RGB image to float
109  representation, converts it to YIQ, rotates the color around the Y channel by
110  delta_hue in radians, scales the chrominance channels (I, Q) by
111  scale_saturation, scales all channels (Y, I, Q) by scale_value,
112  converts back to RGB, and then back to the original data type.
113
114  `image` is an RGB image.  The image hue is adjusted by converting the
115  image to YIQ, rotating around the luminance channel (Y) by
116  `delta_hue` in radians, multiplying the chrominance channels (I, Q)  by
117  `scale_saturation`, and multiplying all channels (Y, I, Q)  by
118  `scale_value`.  The image is then converted back to RGB.
119
120  Args:
121    image: RGB image or images. Size of the last dimension must be 3.
122    delta_hue: float, the hue rotation amount, in radians.
123    scale_saturation: float, factor to multiply the saturation by.
124    scale_value: float, factor to multiply the value by.
125    name: A name for this operation (optional).
126
127  Returns:
128    Adjusted image(s), same shape and DType as `image`.
129  """
130  with ops.name_scope(name, 'adjust_hsv_in_yiq', [image]) as name:
131    image = ops.convert_to_tensor(image, name='image')
132    # Remember original dtype to so we can convert back if needed
133    orig_dtype = image.dtype
134    flt_image = image_ops.convert_image_dtype(image, dtypes.float32)
135
136    rgb_altered = gen_distort_image_ops.adjust_hsv_in_yiq(
137        flt_image, delta_hue, scale_saturation, scale_value)
138
139    return image_ops.convert_image_dtype(rgb_altered, orig_dtype)
140