text: # Copyright 2017 The TensorFlow Authors. All Rights Reserved.
  #
  # 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"""An executable to expand image-level labels, boxes and segments.
  
  The expansion is performed using class hierarchy, provided in JSON file.
  
  The expected file formats are the following:
  - for box and segment files: CSV file is expected to have LabelName field
  - for image-level labels: CSV file is expected to have LabelName and Confidence
  fields
  
  Note, that LabelName is the only field used for expansion.
  
  Example usage:
  python models/research/object_detection/dataset_tools/\
  oid_hierarchical_labels_expansion.py \
  --json_hierarchy_file=<path to JSON hierarchy> \
  --input_annotations=<input csv file> \
  --output_annotations=<output csv file> \
  --annotation_type=<1 (for boxes and segments) or 2 (for image-level labels)>
  """
  
  from __future__ import print_function
  
  import copy
  import json
  from absl import app
  from absl import flags
  
  flags.DEFINE_string(
      'json_hierarchy_file', None,
      'Path to the file containing label hierarchy in JSON format.')
  flags.DEFINE_string(
      'input_annotations', None, 'Path to Open Images annotations file'
      '(either bounding boxes, segments or image-level labels).')
  flags.DEFINE_string('output_annotations', None, 'Path to the output file.')
  flags.DEFINE_integer(
      'annotation_type', None,
      'Type of the input annotations: 1 - boxes or segments,'
      '2 - image-level labels.'
  )
  
  FLAGS = flags.FLAGS
  
  
  def _update_dict(initial_dict, update):
    """Updates dictionary with update content.
  
    Args:
     initial_dict: initial dictionary.
     update: updated dictionary.
    """
  
    for key, value_list in update.items():
      if key in initial_dict:
        initial_dict[key].update(value_list)
      else:
        initial_dict[key] = set(value_list)
  
  
  def _build_plain_hierarchy(hierarchy, skip_root=False):
    """Expands tree hierarchy representation to parent-child dictionary.
  
    Args:
     hierarchy: labels hierarchy as JSON file.
     skip_root: if true skips root from the processing (done for the case when all
       classes under hierarchy are collected under virtual node).
  
    Returns:
      keyed_parent - dictionary of parent - all its children nodes.
      keyed_child  - dictionary of children - all its parent nodes
      children - all children of the current node.
    """
    all_children = set([])
    all_keyed_parent = {}
    all_keyed_child = {}
    if 'Subcategory' in hierarchy:
      for node in hierarchy['Subcategory']:
        keyed_parent, keyed_child, children = _build_plain_hierarchy(node)
        # Update is not done through dict.update() since some children have multi-
        # ple parents in the hiearchy.
        _update_dict(all_keyed_parent, keyed_parent)
        _update_dict(all_keyed_child, keyed_child)
        all_children.update(children)
  
    if not skip_root:
      all_keyed_parent[hierarchy['LabelName']] = copy.deepcopy(all_children)
      all_children.add(hierarchy['LabelName'])
      for child, _ in all_keyed_child.items():
        all_keyed_child[child].add(hierarchy['LabelName'])
      all_keyed_child[hierarchy['LabelName']] = set([])
  
    return all_keyed_parent, all_keyed_child, all_children
  
  
  class OIDHierarchicalLabelsExpansion(object):
    """ Main class to perform labels hierachical expansion."""
  
    def __init__(self, hierarchy):
      """Constructor.
  
      Args:
        hierarchy: labels hierarchy as JSON object.
      """
  
      self._hierarchy_keyed_parent, self._hierarchy_keyed_child, _ = (
          _build_plain_hierarchy(hierarchy, skip_root=True))
  
    def expand_boxes_or_segments_from_csv(self, csv_row,
                                          labelname_column_index=1):
      """Expands a row containing bounding boxes/segments from CSV file.
  
      Args:
        csv_row: a single row of Open Images released groundtruth file.
        labelname_column_index: 0-based index of LabelName column in CSV file.
  
      Returns:
        a list of strings (including the initial row) corresponding to the ground
        truth expanded to multiple annotation for evaluation with Open Images
        Challenge 2018/2019 metrics.
      """
      # Row header is expected to be the following for boxes:
      # ImageID,LabelName,Confidence,XMin,XMax,YMin,YMax,IsGroupOf
      # Row header is expected to be the following for segments:
      # ImageID,LabelName,ImageWidth,ImageHeight,XMin,XMax,YMin,YMax,
      # IsGroupOf,Mask
      split_csv_row = csv_row.split(',')
      result = [csv_row]
      assert split_csv_row[
          labelname_column_index] in self._hierarchy_keyed_child
      parent_nodes = self._hierarchy_keyed_child[
          split_csv_row[labelname_column_index]]
      for parent_node in parent_nodes:
        split_csv_row[labelname_column_index] = parent_node
        result.append(','.join(split_csv_row))
      return result
  
    def expand_labels_from_csv(self,
                               csv_row,
                               labelname_column_index=1,
                               confidence_column_index=2):
      """Expands a row containing labels from CSV file.
  
      Args:
        csv_row: a single row of Open Images released groundtruth file.
        labelname_column_index: 0-based index of LabelName column in CSV file.
        confidence_column_index: 0-based index of Confidence column in CSV file.
  
      Returns:
        a list of strings (including the initial row) corresponding to the ground
        truth expanded to multiple annotation for evaluation with Open Images
        Challenge 2018/2019 metrics.
      """
      # Row header is expected to be exactly:
      # ImageID,Source,LabelName,Confidence
      split_csv_row = csv_row.split(',')
      result = [csv_row]
      if int(split_csv_row[confidence_column_index]) == 1:
        assert split_csv_row[
            labelname_column_index] in self._hierarchy_keyed_child
        parent_nodes = self._hierarchy_keyed_child[
            split_csv_row[labelname_column_index]]
        for parent_node in parent_nodes:
          split_csv_row[labelname_column_index] = parent_node
          result.append(','.join(split_csv_row))
      else:
        assert split_csv_row[
            labelname_column_index] in self._hierarchy_keyed_parent
        child_nodes = self._hierarchy_keyed_parent[
            split_csv_row[labelname_column_index]]
        for child_node in child_nodes:
          split_csv_row[labelname_column_index] = child_node
          result.append(','.join(split_csv_row))
      return result
  
  
  def main(unused_args):
  
    del unused_args
  
    with open(FLAGS.json_hierarchy_file) as f:
      hierarchy = json.load(f)
    expansion_generator = OIDHierarchicalLabelsExpansion(hierarchy)
    labels_file = False
    if FLAGS.annotation_type == 2:
      labels_file = True
    elif FLAGS.annotation_type != 1:
      print('--annotation_type expected value is 1 or 2.')
      return -1
    confidence_column_index = -1
    labelname_column_index = -1
    with open(FLAGS.input_annotations, 'r') as source:
      with open(FLAGS.output_annotations, 'w') as target:
        header = source.readline()
        target.writelines([header])
        column_names = header.strip().split(',')
        labelname_column_index = column_names.index('LabelName')
        if labels_file:
          confidence_column_index = column_names.index('Confidence')
        for line in source:
          if labels_file:
            expanded_lines = expansion_generator.expand_labels_from_csv(
                line, labelname_column_index, confidence_column_index)
          else:
            expanded_lines = (
                expansion_generator.expand_boxes_or_segments_from_csv(
                    line, labelname_column_index))
          target.writelines(expanded_lines)
  
  
  if __name__ == '__main__':
    flags.mark_flag_as_required('json_hierarchy_file')
    flags.mark_flag_as_required('input_annotations')
    flags.mark_flag_as_required('output_annotations')
    flags.mark_flag_as_required('annotation_type')
  
    app.run(main)
  