|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"""Builder for preprocessing steps.""" |
|
|
|
import tensorflow as tf |
|
|
|
from object_detection.core import preprocessor |
|
from object_detection.protos import preprocessor_pb2 |
|
|
|
|
|
def _get_step_config_from_proto(preprocessor_step_config, step_name): |
|
"""Returns the value of a field named step_name from proto. |
|
|
|
Args: |
|
preprocessor_step_config: A preprocessor_pb2.PreprocessingStep object. |
|
step_name: Name of the field to get value from. |
|
|
|
Returns: |
|
result_dict: a sub proto message from preprocessor_step_config which will be |
|
later converted to a dictionary. |
|
|
|
Raises: |
|
ValueError: If field does not exist in proto. |
|
""" |
|
for field, value in preprocessor_step_config.ListFields(): |
|
if field.name == step_name: |
|
return value |
|
|
|
raise ValueError('Could not get field %s from proto!', step_name) |
|
|
|
|
|
def _get_dict_from_proto(config): |
|
"""Helper function to put all proto fields into a dictionary. |
|
|
|
For many preprocessing steps, there's an trivial 1-1 mapping from proto fields |
|
to function arguments. This function automatically populates a dictionary with |
|
the arguments from the proto. |
|
|
|
Protos that CANNOT be trivially populated include: |
|
* nested messages. |
|
* steps that check if an optional field is set (ie. where None != 0). |
|
* protos that don't map 1-1 to arguments (ie. list should be reshaped). |
|
* fields requiring additional validation (ie. repeated field has n elements). |
|
|
|
Args: |
|
config: A protobuf object that does not violate the conditions above. |
|
|
|
Returns: |
|
result_dict: |config| converted into a python dictionary. |
|
""" |
|
result_dict = {} |
|
for field, value in config.ListFields(): |
|
result_dict[field.name] = value |
|
return result_dict |
|
|
|
|
|
|
|
|
|
|
|
PREPROCESSING_FUNCTION_MAP = { |
|
'normalize_image': |
|
preprocessor.normalize_image, |
|
'random_pixel_value_scale': |
|
preprocessor.random_pixel_value_scale, |
|
'random_image_scale': |
|
preprocessor.random_image_scale, |
|
'random_rgb_to_gray': |
|
preprocessor.random_rgb_to_gray, |
|
'random_adjust_brightness': |
|
preprocessor.random_adjust_brightness, |
|
'random_adjust_contrast': |
|
preprocessor.random_adjust_contrast, |
|
'random_adjust_hue': |
|
preprocessor.random_adjust_hue, |
|
'random_adjust_saturation': |
|
preprocessor.random_adjust_saturation, |
|
'random_distort_color': |
|
preprocessor.random_distort_color, |
|
'random_jitter_boxes': |
|
preprocessor.random_jitter_boxes, |
|
'random_crop_to_aspect_ratio': |
|
preprocessor.random_crop_to_aspect_ratio, |
|
'random_black_patches': |
|
preprocessor.random_black_patches, |
|
'rgb_to_gray': |
|
preprocessor.rgb_to_gray, |
|
'scale_boxes_to_pixel_coordinates': ( |
|
preprocessor.scale_boxes_to_pixel_coordinates), |
|
'subtract_channel_mean': |
|
preprocessor.subtract_channel_mean, |
|
'convert_class_logits_to_softmax': |
|
preprocessor.convert_class_logits_to_softmax, |
|
} |
|
|
|
|
|
|
|
|
|
RESIZE_METHOD_MAP = { |
|
preprocessor_pb2.ResizeImage.AREA: tf.image.ResizeMethod.AREA, |
|
preprocessor_pb2.ResizeImage.BICUBIC: tf.image.ResizeMethod.BICUBIC, |
|
preprocessor_pb2.ResizeImage.BILINEAR: tf.image.ResizeMethod.BILINEAR, |
|
preprocessor_pb2.ResizeImage.NEAREST_NEIGHBOR: ( |
|
tf.image.ResizeMethod.NEAREST_NEIGHBOR), |
|
} |
|
|
|
|
|
def build(preprocessor_step_config): |
|
"""Builds preprocessing step based on the configuration. |
|
|
|
Args: |
|
preprocessor_step_config: PreprocessingStep configuration proto. |
|
|
|
Returns: |
|
function, argmap: A callable function and an argument map to call function |
|
with. |
|
|
|
Raises: |
|
ValueError: On invalid configuration. |
|
""" |
|
step_type = preprocessor_step_config.WhichOneof('preprocessing_step') |
|
|
|
if step_type in PREPROCESSING_FUNCTION_MAP: |
|
preprocessing_function = PREPROCESSING_FUNCTION_MAP[step_type] |
|
step_config = _get_step_config_from_proto(preprocessor_step_config, |
|
step_type) |
|
function_args = _get_dict_from_proto(step_config) |
|
return (preprocessing_function, function_args) |
|
|
|
if step_type == 'random_horizontal_flip': |
|
config = preprocessor_step_config.random_horizontal_flip |
|
return (preprocessor.random_horizontal_flip, |
|
{ |
|
'keypoint_flip_permutation': tuple( |
|
config.keypoint_flip_permutation), |
|
}) |
|
|
|
if step_type == 'random_vertical_flip': |
|
config = preprocessor_step_config.random_vertical_flip |
|
return (preprocessor.random_vertical_flip, |
|
{ |
|
'keypoint_flip_permutation': tuple( |
|
config.keypoint_flip_permutation), |
|
}) |
|
|
|
if step_type == 'random_rotation90': |
|
return (preprocessor.random_rotation90, {}) |
|
|
|
if step_type == 'random_crop_image': |
|
config = preprocessor_step_config.random_crop_image |
|
return (preprocessor.random_crop_image, |
|
{ |
|
'min_object_covered': config.min_object_covered, |
|
'aspect_ratio_range': (config.min_aspect_ratio, |
|
config.max_aspect_ratio), |
|
'area_range': (config.min_area, config.max_area), |
|
'overlap_thresh': config.overlap_thresh, |
|
'clip_boxes': config.clip_boxes, |
|
'random_coef': config.random_coef, |
|
}) |
|
|
|
if step_type == 'random_pad_image': |
|
config = preprocessor_step_config.random_pad_image |
|
min_image_size = None |
|
if (config.HasField('min_image_height') != |
|
config.HasField('min_image_width')): |
|
raise ValueError('min_image_height and min_image_width should be either ' |
|
'both set or both unset.') |
|
if config.HasField('min_image_height'): |
|
min_image_size = (config.min_image_height, config.min_image_width) |
|
|
|
max_image_size = None |
|
if (config.HasField('max_image_height') != |
|
config.HasField('max_image_width')): |
|
raise ValueError('max_image_height and max_image_width should be either ' |
|
'both set or both unset.') |
|
if config.HasField('max_image_height'): |
|
max_image_size = (config.max_image_height, config.max_image_width) |
|
|
|
pad_color = config.pad_color or None |
|
if pad_color: |
|
if len(pad_color) != 3: |
|
tf.logging.warn('pad_color should have 3 elements (RGB) if set!') |
|
|
|
pad_color = tf.to_float([x for x in config.pad_color]) |
|
return (preprocessor.random_pad_image, |
|
{ |
|
'min_image_size': min_image_size, |
|
'max_image_size': max_image_size, |
|
'pad_color': pad_color, |
|
}) |
|
|
|
if step_type == 'random_absolute_pad_image': |
|
config = preprocessor_step_config.random_absolute_pad_image |
|
|
|
max_height_padding = config.max_height_padding or 1 |
|
max_width_padding = config.max_width_padding or 1 |
|
|
|
pad_color = config.pad_color or None |
|
if pad_color: |
|
if len(pad_color) != 3: |
|
tf.logging.warn('pad_color should have 3 elements (RGB) if set!') |
|
|
|
pad_color = tf.to_float([x for x in config.pad_color]) |
|
|
|
return (preprocessor.random_absolute_pad_image, |
|
{ |
|
'max_height_padding': max_height_padding, |
|
'max_width_padding': max_width_padding, |
|
'pad_color': pad_color, |
|
}) |
|
if step_type == 'random_crop_pad_image': |
|
config = preprocessor_step_config.random_crop_pad_image |
|
min_padded_size_ratio = config.min_padded_size_ratio |
|
if min_padded_size_ratio and len(min_padded_size_ratio) != 2: |
|
raise ValueError('min_padded_size_ratio should have 2 elements if set!') |
|
max_padded_size_ratio = config.max_padded_size_ratio |
|
if max_padded_size_ratio and len(max_padded_size_ratio) != 2: |
|
raise ValueError('max_padded_size_ratio should have 2 elements if set!') |
|
pad_color = config.pad_color or None |
|
if pad_color: |
|
if len(pad_color) != 3: |
|
tf.logging.warn('pad_color should have 3 elements (RGB) if set!') |
|
|
|
pad_color = tf.to_float([x for x in config.pad_color]) |
|
|
|
kwargs = { |
|
'min_object_covered': config.min_object_covered, |
|
'aspect_ratio_range': (config.min_aspect_ratio, |
|
config.max_aspect_ratio), |
|
'area_range': (config.min_area, config.max_area), |
|
'overlap_thresh': config.overlap_thresh, |
|
'clip_boxes': config.clip_boxes, |
|
'random_coef': config.random_coef, |
|
'pad_color': pad_color, |
|
} |
|
if min_padded_size_ratio: |
|
kwargs['min_padded_size_ratio'] = tuple(min_padded_size_ratio) |
|
if max_padded_size_ratio: |
|
kwargs['max_padded_size_ratio'] = tuple(max_padded_size_ratio) |
|
return (preprocessor.random_crop_pad_image, kwargs) |
|
|
|
if step_type == 'random_resize_method': |
|
config = preprocessor_step_config.random_resize_method |
|
return (preprocessor.random_resize_method, |
|
{ |
|
'target_size': [config.target_height, config.target_width], |
|
}) |
|
|
|
if step_type == 'resize_image': |
|
config = preprocessor_step_config.resize_image |
|
method = RESIZE_METHOD_MAP[config.method] |
|
return (preprocessor.resize_image, |
|
{ |
|
'new_height': config.new_height, |
|
'new_width': config.new_width, |
|
'method': method |
|
}) |
|
|
|
if step_type == 'random_self_concat_image': |
|
config = preprocessor_step_config.random_self_concat_image |
|
return (preprocessor.random_self_concat_image, { |
|
'concat_vertical_probability': config.concat_vertical_probability, |
|
'concat_horizontal_probability': config.concat_horizontal_probability |
|
}) |
|
|
|
if step_type == 'ssd_random_crop': |
|
config = preprocessor_step_config.ssd_random_crop |
|
if config.operations: |
|
min_object_covered = [op.min_object_covered for op in config.operations] |
|
aspect_ratio_range = [(op.min_aspect_ratio, op.max_aspect_ratio) |
|
for op in config.operations] |
|
area_range = [(op.min_area, op.max_area) for op in config.operations] |
|
overlap_thresh = [op.overlap_thresh for op in config.operations] |
|
clip_boxes = [op.clip_boxes for op in config.operations] |
|
random_coef = [op.random_coef for op in config.operations] |
|
return (preprocessor.ssd_random_crop, |
|
{ |
|
'min_object_covered': min_object_covered, |
|
'aspect_ratio_range': aspect_ratio_range, |
|
'area_range': area_range, |
|
'overlap_thresh': overlap_thresh, |
|
'clip_boxes': clip_boxes, |
|
'random_coef': random_coef, |
|
}) |
|
return (preprocessor.ssd_random_crop, {}) |
|
|
|
if step_type == 'ssd_random_crop_pad': |
|
config = preprocessor_step_config.ssd_random_crop_pad |
|
if config.operations: |
|
min_object_covered = [op.min_object_covered for op in config.operations] |
|
aspect_ratio_range = [(op.min_aspect_ratio, op.max_aspect_ratio) |
|
for op in config.operations] |
|
area_range = [(op.min_area, op.max_area) for op in config.operations] |
|
overlap_thresh = [op.overlap_thresh for op in config.operations] |
|
clip_boxes = [op.clip_boxes for op in config.operations] |
|
random_coef = [op.random_coef for op in config.operations] |
|
min_padded_size_ratio = [tuple(op.min_padded_size_ratio) |
|
for op in config.operations] |
|
max_padded_size_ratio = [tuple(op.max_padded_size_ratio) |
|
for op in config.operations] |
|
pad_color = [(op.pad_color_r, op.pad_color_g, op.pad_color_b) |
|
for op in config.operations] |
|
return (preprocessor.ssd_random_crop_pad, |
|
{ |
|
'min_object_covered': min_object_covered, |
|
'aspect_ratio_range': aspect_ratio_range, |
|
'area_range': area_range, |
|
'overlap_thresh': overlap_thresh, |
|
'clip_boxes': clip_boxes, |
|
'random_coef': random_coef, |
|
'min_padded_size_ratio': min_padded_size_ratio, |
|
'max_padded_size_ratio': max_padded_size_ratio, |
|
'pad_color': pad_color, |
|
}) |
|
return (preprocessor.ssd_random_crop_pad, {}) |
|
|
|
if step_type == 'ssd_random_crop_fixed_aspect_ratio': |
|
config = preprocessor_step_config.ssd_random_crop_fixed_aspect_ratio |
|
if config.operations: |
|
min_object_covered = [op.min_object_covered for op in config.operations] |
|
area_range = [(op.min_area, op.max_area) for op in config.operations] |
|
overlap_thresh = [op.overlap_thresh for op in config.operations] |
|
clip_boxes = [op.clip_boxes for op in config.operations] |
|
random_coef = [op.random_coef for op in config.operations] |
|
return (preprocessor.ssd_random_crop_fixed_aspect_ratio, |
|
{ |
|
'min_object_covered': min_object_covered, |
|
'aspect_ratio': config.aspect_ratio, |
|
'area_range': area_range, |
|
'overlap_thresh': overlap_thresh, |
|
'clip_boxes': clip_boxes, |
|
'random_coef': random_coef, |
|
}) |
|
return (preprocessor.ssd_random_crop_fixed_aspect_ratio, {}) |
|
|
|
if step_type == 'ssd_random_crop_pad_fixed_aspect_ratio': |
|
config = preprocessor_step_config.ssd_random_crop_pad_fixed_aspect_ratio |
|
kwargs = {} |
|
aspect_ratio = config.aspect_ratio |
|
if aspect_ratio: |
|
kwargs['aspect_ratio'] = aspect_ratio |
|
min_padded_size_ratio = config.min_padded_size_ratio |
|
if min_padded_size_ratio: |
|
if len(min_padded_size_ratio) != 2: |
|
raise ValueError('min_padded_size_ratio should have 2 elements if set!') |
|
kwargs['min_padded_size_ratio'] = tuple(min_padded_size_ratio) |
|
max_padded_size_ratio = config.max_padded_size_ratio |
|
if max_padded_size_ratio: |
|
if len(max_padded_size_ratio) != 2: |
|
raise ValueError('max_padded_size_ratio should have 2 elements if set!') |
|
kwargs['max_padded_size_ratio'] = tuple(max_padded_size_ratio) |
|
if config.operations: |
|
kwargs['min_object_covered'] = [op.min_object_covered |
|
for op in config.operations] |
|
kwargs['aspect_ratio_range'] = [(op.min_aspect_ratio, op.max_aspect_ratio) |
|
for op in config.operations] |
|
kwargs['area_range'] = [(op.min_area, op.max_area) |
|
for op in config.operations] |
|
kwargs['overlap_thresh'] = [op.overlap_thresh for op in config.operations] |
|
kwargs['clip_boxes'] = [op.clip_boxes for op in config.operations] |
|
kwargs['random_coef'] = [op.random_coef for op in config.operations] |
|
return (preprocessor.ssd_random_crop_pad_fixed_aspect_ratio, kwargs) |
|
|
|
raise ValueError('Unknown preprocessing step.') |
|
|