Spaces:
Runtime error
Runtime error
# coding=utf-8 | |
# Copyright 2021 The Deeplab2 Authors. | |
# | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
r"""Script to export deeplab model to saved model.""" | |
import functools | |
from typing import Any, MutableMapping, Sequence, Text | |
from absl import app | |
from absl import flags | |
import tensorflow as tf | |
from google.protobuf import text_format | |
from deeplab2 import config_pb2 | |
from deeplab2.data import dataset | |
from deeplab2.data.preprocessing import input_preprocessing | |
from deeplab2.model import utils | |
from deeplab2.trainer import train_lib | |
_FLAGS_EXPERIMENT_OPTION_PATH = flags.DEFINE_string( | |
'experiment_option_path', | |
default='', | |
help='Path to the experiment option text proto.') | |
_FLAGS_CKPT_PATH = flags.DEFINE_string( | |
'checkpoint_path', | |
default='', | |
help='Path to the saved checkpoint.') | |
_FLAGS_OUTPUT_PATH = flags.DEFINE_string( | |
'output_path', | |
default='', | |
help='Output directory path for the exported saved model.') | |
_FLAGS_MERGE_WITH_TF_OP = flags.DEFINE_boolean( | |
'merge_with_tf_op', | |
default=False, | |
help='Whether to use customized TF op for merge semantic and instance ' | |
'predictions. Set it to True to reproduce the numbers as reported in ' | |
'paper, but the saved model would require specifically compiled TensorFlow ' | |
'to run.') | |
class DeepLabModule(tf.Module): | |
"""Class that runs DeepLab inference end-to-end.""" | |
def __init__(self, config: config_pb2.ExperimentOptions, ckpt_path: Text, | |
use_tf_op: bool = False): | |
super().__init__(name='DeepLabModule') | |
dataset_options = config.eval_dataset_options | |
dataset_name = dataset_options.dataset | |
crop_height, crop_width = dataset_options.crop_size | |
config.evaluator_options.merge_semantic_and_instance_with_tf_op = use_tf_op | |
# Disable drop path and recompute grad as they are only used in training. | |
config.model_options.backbone.drop_path_keep_prob = 1.0 | |
deeplab_model = train_lib.create_deeplab_model( | |
config, | |
dataset.MAP_NAME_TO_DATASET_INFO[dataset_name]) | |
self._is_motion_deeplab = ( | |
config.model_options.WhichOneof('meta_architecture') == | |
'motion_deeplab') | |
# For now we only support batch size of 1 for saved model. | |
input_shape = train_lib.build_deeplab_model( | |
deeplab_model, (crop_height, crop_width), batch_size=1) | |
self._input_depth = input_shape[-1] | |
checkpoint = tf.train.Checkpoint(**deeplab_model.checkpoint_items) | |
# Not all saved variables (e.g. variables from optimizer) will be restored. | |
# `expect_partial()` to suppress the warning. | |
checkpoint.restore(ckpt_path).expect_partial() | |
self._model = deeplab_model | |
self._preprocess_fn = functools.partial( | |
input_preprocessing.preprocess_image_and_label, | |
label=None, | |
crop_height=crop_height, | |
crop_width=crop_width, | |
prev_label=None, | |
min_resize_value=dataset_options.min_resize_value, | |
max_resize_value=dataset_options.max_resize_value, | |
resize_factor=dataset_options.resize_factor, | |
is_training=False) | |
def get_input_spec(self): | |
"""Returns TensorSpec of input tensor needed for inference.""" | |
# We expect a single 3D, uint8 tensor with shape [height, width, channels]. | |
return tf.TensorSpec(shape=[None, None, self._input_depth], dtype=tf.uint8) | |
def __call__(self, input_tensor: tf.Tensor) -> MutableMapping[Text, Any]: | |
"""Performs a forward pass. | |
Args: | |
input_tensor: An uint8 input tensor of type tf.Tensor with shape [height, | |
width, channels]. | |
Returns: | |
A dictionary containing the results of the specified DeepLab architecture. | |
The results are bilinearly upsampled to input size before returning. | |
""" | |
input_size = [tf.shape(input_tensor)[0], tf.shape(input_tensor)[1]] | |
if self._is_motion_deeplab: | |
# For motion deeplab, split the input tensor to current and previous | |
# frame before preprocessing, and re-assemble them. | |
image, prev_image = tf.split(input_tensor, 2, axis=2) | |
(resized_image, processed_image, _, processed_prev_image, | |
_) = self._preprocess_fn(image=image, prev_image=prev_image) | |
processed_image = tf.concat( | |
[processed_image, processed_prev_image], axis=2) | |
else: | |
(resized_image, processed_image, _, _, _) = self._preprocess_fn( | |
image=input_tensor) | |
resized_size = tf.shape(resized_image)[0:2] | |
# Making input tensor to 4D to fit model input requirements. | |
outputs = self._model(tf.expand_dims(processed_image, 0), training=False) | |
# We only undo-preprocess for those defined in tuples in model/utils.py. | |
return utils.undo_preprocessing(outputs, resized_size, | |
input_size) | |
def main(argv: Sequence[str]) -> None: | |
if len(argv) > 1: | |
raise app.UsageError('Too many command-line arguments.') | |
config = config_pb2.ExperimentOptions() | |
with tf.io.gfile.GFile(_FLAGS_EXPERIMENT_OPTION_PATH.value, 'r') as f: | |
text_format.Parse(f.read(), config) | |
module = DeepLabModule( | |
config, _FLAGS_CKPT_PATH.value, _FLAGS_MERGE_WITH_TF_OP.value) | |
signatures = module.__call__.get_concrete_function(module.get_input_spec()) | |
tf.saved_model.save( | |
module, _FLAGS_OUTPUT_PATH.value, signatures=signatures) | |
if __name__ == '__main__': | |
app.run(main) | |